From 964f1443403418f005b1269459ce69d376b64f1e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 31 Jul 2026 17:49:23 +0000 Subject: [PATCH 01/72] [maven-release-plugin] prepare for next development iteration --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index dd88d2405..96770084b 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.9-preview.2 + 1.0.10-preview.2-SNAPSHOT jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.9-preview.2 + HEAD From 9996d5630d8a2bdc648160d66d4745222b557be1 Mon Sep 17 00:00:00 2001 From: Josh Spicer <23246594+joshspicer@users.noreply.github.com> Date: Fri, 31 Jul 2026 13:25:14 -0700 Subject: [PATCH 02/72] Expose managed approval requirement on permission requests (#2080) * Expose managed approval requirement on permission requests Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 18f1bbc1-6001-43e2-b293-724505087f6a * docs: align managed selector name with Domain Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c7f00b84-b0a7-4cdf-aca9-ffd49737f26e * Fix managed permission approval surfaces Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: clarify managed approval behavior Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: guard managed custom approvals Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Expose managed approvals across SDKs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Respect managed approval in permission policies Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Minimize Java permission overlay Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify Java managed permission deferral Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve Rust permission event metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix managed approval C# example Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix managed approval documentation guards Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix .NET managed permission event test Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fail approve-all in managed sessions Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Avoid expanding Rust permission result API Preserve approve-all fail-closed behavior by logging and returning the existing user-not-available decision instead of adding a public enum variant. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Keep managed permission helpers fail-closed Align Rust approve-all paths for request-level managed approval and correct cross-SDK documentation of session-level fail-fast behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix Java Gradle snapshot coordinate Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Align managed permission documentation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify Java managed approval handling Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Surface permission handler failures Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Log Java permission handler failures Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify permission failure diagnostics Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify Rust managed approval fallback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Reject no-result in legacy Python callbacks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fail closed on managed approval metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Initialize managed settings before Go events Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fail closed on unknown permission requests Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve legacy Python approve-all calls Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify Rust permission event payload Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Harden managed permission fallbacks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Regenerate managed approval outputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Regenerate managed approval outputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Align managed approval helpers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix managed approval hierarchy and diagnostics Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Restore managed settings approval guard Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fail closed on malformed managed metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve managed approval accessor compatibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve Python permission constructor order Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Preserve managed metadata compatibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fail closed on malformed Java managed metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Retrigger CI Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: GitHub Copilot Copilot-Session: 18f1bbc1-6001-43e2-b293-724505087f6a Copilot-Session: c7f00b84-b0a7-4cdf-aca9-ffd49737f26e --- dotnet/README.md | 15 ++- dotnet/src/Client.cs | 4 +- dotnet/src/Generated/SessionEvents.cs | 37 +++-- dotnet/src/PermissionHandlers.cs | 31 ++++- dotnet/src/Session.cs | 16 ++- dotnet/src/Types.cs | 3 + dotnet/test/Unit/PermissionHandlerTests.cs | 127 ++++++++++++++++++ go/README.md | 13 +- go/client.go | 14 +- go/permissions.go | 12 +- go/permissions_test.go | 82 +++++++++++ go/rpc/permission_request_managed_approval.go | 81 +++++++++++ go/rpc/zsession_events.go | 13 ++ go/session.go | 13 +- go/types.go | 3 +- java/README.md | 25 +++- .../com/github/copilot/CopilotSession.java | 8 ++ .../github/copilot/SessionRequestBuilder.java | 2 + .../github/copilot/rpc/PermissionHandler.java | 25 +++- .../copilot/rpc/PermissionInvocation.java | 22 +++ .../github/copilot/rpc/PermissionRequest.java | 68 ++++++++++ .../copilot/PermissionRequestResultTest.java | 92 +++++++++++++ nodejs/README.md | 17 ++- nodejs/src/client.ts | 10 +- nodejs/src/index.ts | 15 ++- nodejs/src/session.ts | 12 +- nodejs/src/types.ts | 48 ++++++- nodejs/test/client.test.ts | 34 +++++ nodejs/test/session-event-types.test.ts | 87 ++++++++++++ python/README.md | 23 +++- python/copilot/client.py | 14 +- python/copilot/generated/session_events.py | 30 +++++ python/copilot/session.py | 63 +++++++-- python/test_managed_permissions.py | 121 +++++++++++++++++ rust/README.md | 8 +- rust/src/generated/session_events.rs | 18 +++ rust/src/handler.rs | 59 +++++++- rust/src/permission.rs | 54 +++++++- rust/src/session.rs | 119 ++++++++++++++-- rust/src/types.rs | 11 +- rust/tests/api_types_test.rs | 20 +++ scripts/codegen/csharp.ts | 61 ++++++++- scripts/codegen/go.ts | 8 +- scripts/codegen/python.ts | 17 ++- scripts/codegen/rust.ts | 5 +- scripts/codegen/utils.ts | 42 ++++++ 46 files changed, 1498 insertions(+), 104 deletions(-) create mode 100644 dotnet/test/Unit/PermissionHandlerTests.cs create mode 100644 go/permissions_test.go create mode 100644 go/rpc/permission_request_managed_approval.go create mode 100644 python/test_managed_permissions.py diff --git a/dotnet/README.md b/dotnet/README.md index 1f71926dc..1971c2107 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -37,7 +37,7 @@ using GitHub.Copilot; await using var client = new CopilotClient(); await client.StartAsync(); -// Create a session (OnPermissionRequest is optional; ApproveAll allows every tool) +// ApproveAll is only valid when managed settings are disabled. await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5", @@ -132,7 +132,7 @@ Create a new conversation session. - `Streaming` - Enable streaming of response chunks (default: false) - `InfiniteSessions` - Configure automatic context compaction (see below) - `EnableSessionStore` - Enables the cross-session store for search and retrieval across sessions. When unset in `CopilotClientMode.CopilotCli`, the runtime default applies (enabled). In `CopilotClientMode.Empty`, defaults to disabled. -- `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. Use `PermissionHandler.ApproveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. +- `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.ApproveAll` approves requests when managed settings are disabled and throws when `EnableManagedSettings` is true. Custom handlers can inspect `ManagedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` - Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `Hooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. @@ -782,7 +782,7 @@ An `OnPermissionRequest` handler is optional when you create or resume a session ### Approve All (simplest) -Use the built-in `PermissionHandler.ApproveAll` helper to allow every tool call without any checks: +Use the built-in `PermissionHandler.ApproveAll` helper to approve ordinary permission requests automatically: ```csharp using GitHub.Copilot; @@ -794,9 +794,11 @@ var session = await client.CreateSessionAsync(new SessionConfig }); ``` +When `EnableManagedSettings` is true for the session, `ApproveAll` throws on the first permission request. Use a custom handler for managed sessions; request-level `ManagedApprovalRequired` remains available for human-facing confirmation logic. + ### Custom Permission Handler -Provide your own permission handler (`Func>`) to inspect each request and apply custom logic: +Provide your own permission handler (`Func>`) to inspect each request and apply custom logic. Check `ManagedApprovalRequired` before any automatic approval: ```csharp var session = await client.CreateSessionAsync(new SessionConfig @@ -804,6 +806,11 @@ var session = await client.CreateSessionAsync(new SessionConfig Model = "gpt-5", OnPermissionRequest = async (request, invocation) => { + if (request.ManagedApprovalRequired is true) + { + return PermissionDecision.NoResult(); + } + // Pattern-match on the discriminated PermissionRequest union to access // per-kind fields (FullCommandText, Path, ToolName, …). return request switch diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 82511ea0d..595aa3356 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -783,7 +783,9 @@ private CopilotSession InitializeSession( _logger, this); session.RegisterTools(config.Tools ?? []); - session.RegisterPermissionHandler(config.OnPermissionRequest); + session.RegisterPermissionHandler( + config.OnPermissionRequest, + config.EnableManagedSettings is true); session.RegisterMcpAuthHandler(config.OnMcpAuthRequest); session.RegisterCommands(config.Commands); session.RegisterElicitationHandler(config.OnElicitationRequest); diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 5f89d3195..b66193247 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -6956,10 +6956,14 @@ public sealed partial class PermissionRequestShell : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } - /// Whether managed policy requires a human response and forbids host auto-approval. + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("managedApprovalRequired")] - public bool? ManagedApprovalRequired { get; set; } + public override bool? ManagedApprovalRequired + { + get => base.ManagedApprovalRequired; + set => base.ManagedApprovalRequired = value; + } /// File paths that may be read or written by the command. [JsonPropertyName("possiblePaths")] @@ -7014,10 +7018,14 @@ public sealed partial class PermissionRequestWrite : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } - /// Whether managed policy requires a human response and forbids host auto-approval. + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("managedApprovalRequired")] - public bool? ManagedApprovalRequired { get; set; } + public override bool? ManagedApprovalRequired + { + get => base.ManagedApprovalRequired; + set => base.ManagedApprovalRequired = value; + } /// Complete new file contents for newly created files. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -7052,10 +7060,14 @@ public sealed partial class PermissionRequestRead : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } - /// Whether managed policy requires a human response and forbids host auto-approval. + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("managedApprovalRequired")] - public bool? ManagedApprovalRequired { get; set; } + public override bool? ManagedApprovalRequired + { + get => base.ManagedApprovalRequired; + set => base.ManagedApprovalRequired = value; + } /// Path of the file or directory being read. [JsonPropertyName("path")] @@ -7124,10 +7136,14 @@ public sealed partial class PermissionRequestUrl : PermissionRequest [JsonPropertyName("intention")] public required string Intention { get; set; } - /// Whether managed policy requires a human response and forbids host auto-approval. + /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("managedApprovalRequired")] - public bool? ManagedApprovalRequired { get; set; } + public override bool? ManagedApprovalRequired + { + get => base.ManagedApprovalRequired; + set => base.ManagedApprovalRequired = value; + } /// Immediately preceding URL when this request is for a redirect target. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -7317,6 +7333,11 @@ public partial class PermissionRequest /// The type discriminator. [JsonPropertyName("kind")] public virtual string Kind { get; set; } = string.Empty; + + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public virtual bool? ManagedApprovalRequired { get; set; } } diff --git a/dotnet/src/PermissionHandlers.cs b/dotnet/src/PermissionHandlers.cs index 4386e8ba6..d990ca653 100644 --- a/dotnet/src/PermissionHandlers.cs +++ b/dotnet/src/PermissionHandlers.cs @@ -9,7 +9,34 @@ namespace GitHub.Copilot; /// Provides pre-built permission request handlers. public static class PermissionHandler { - /// A permission handler that approves all permission requests. + /// + /// A permission handler that approves requests when managed settings are disabled. + /// public static Func> ApproveAll { get; } = - (_, _) => Task.FromResult(PermissionDecision.ApproveOnce()); + (request, invocation) => invocation.ManagedSettingsEnabled + ? Task.FromException( + new InvalidOperationException("ApproveAll cannot be used when managed settings are enabled")) + : RequiresManagedApproval(request) + ? Task.FromResult(PermissionDecision.NoResult()) + : Task.FromResult(PermissionDecision.ApproveOnce()); + + private static bool RequiresManagedApproval(PermissionRequest request) + { + if (request.ManagedApprovalRequired is true) + { + return true; + } + + return request.GetType() == typeof(PermissionRequest) + && request.Kind is not ("shell" + or "write" + or "read" + or "mcp" + or "url" + or "memory" + or "custom-tool" + or "hook" + or "extension-management" + or "extension-permission-access"); + } } diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index 6db0744b4..d2f6cedbf 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -63,6 +63,7 @@ public sealed partial class CopilotSession : IAsyncDisposable private readonly CopilotClient _parentClient; private volatile Func>? _permissionHandler; + private bool _managedSettingsEnabled; private volatile Func>? _mcpAuthHandler; private volatile Func>? _userInputHandler; private volatile Func>? _elicitationHandler; @@ -557,13 +558,17 @@ internal void RegisterTools(ICollection tools) /// Registers a handler for permission requests. /// /// The permission handler function. + /// Whether managed settings are enabled for the session. /// /// When the assistant needs permission to perform certain actions (e.g., file operations), /// this handler is called to approve or deny the request. /// - internal void RegisterPermissionHandler(Func>? handler) + internal void RegisterPermissionHandler( + Func>? handler, + bool managedSettingsEnabled) { _permissionHandler = handler; + _managedSettingsEnabled = managedSettingsEnabled; } internal void RegisterMcpAuthHandler(Func>? handler) @@ -590,7 +595,8 @@ internal async Task HandlePermissionRequestAsync(JsonElement var invocation = new PermissionInvocation { - SessionId = SessionId + SessionId = SessionId, + ManagedSettingsEnabled = _managedSettingsEnabled }; var permissionTimestamp = Stopwatch.GetTimestamp(); @@ -932,7 +938,8 @@ private async Task ExecutePermissionAndRespondAsync(string requestId, Permission { var invocation = new PermissionInvocation { - SessionId = SessionId + SessionId = SessionId, + ManagedSettingsEnabled = _managedSettingsEnabled }; var permissionTimestamp = Stopwatch.GetTimestamp(); @@ -954,8 +961,9 @@ private async Task ExecutePermissionAndRespondAsync(string requestId, Permission SessionId, requestId); } - catch (Exception) + catch (Exception ex) { + _logger.LogError(ex, "Permission handler or response delivery failed. SessionId={SessionId}, RequestId={RequestId}", SessionId, requestId); try { await Rpc.Permissions.HandlePendingPermissionRequestAsync(requestId, PermissionDecision.UserNotAvailable()); diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 565204d39..a5b620702 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -833,6 +833,9 @@ public sealed class PermissionInvocation /// Identifier of the session that triggered the permission request. /// public string SessionId { get; set; } = string.Empty; + + /// Whether managed settings are enabled for this session. + public bool ManagedSettingsEnabled { get; set; } } // ============================================================================ diff --git a/dotnet/test/Unit/PermissionHandlerTests.cs b/dotnet/test/Unit/PermissionHandlerTests.cs new file mode 100644 index 000000000..675ea1282 --- /dev/null +++ b/dotnet/test/Unit/PermissionHandlerTests.cs @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using GitHub.Copilot.Rpc; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public class PermissionHandlerTests +{ + private static readonly JsonSerializerOptions SerializerOptions = new() + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver(), + }; + + [Fact] + public void PermissionEventExposesManagedApprovalRequired() + { + const string json = """ + { + "permissionRequest": { + "kind": "read", + "intention": "Read managed content", + "path": "/workspace/file.txt", + "managedApprovalRequired": true + }, + "requestId": "permission-1" + } + """; + + var data = JsonSerializer.Deserialize( + json, + SerializerOptions); + + Assert.NotNull(data); + var request = Assert.IsType(data.PermissionRequest); + Assert.True(request.ManagedApprovalRequired); + PermissionRequest genericRequest = request; + Assert.True(genericRequest.ManagedApprovalRequired); + } + + [Fact] + public async Task ApproveAllThrowsWhenManagedSettingsEnabled() + { + var request = new PermissionRequest + { + Kind = "read", + ManagedApprovalRequired = true, + }; + + await Assert.ThrowsAsync(() => + PermissionHandler.ApproveAll(request, new PermissionInvocation + { + ManagedSettingsEnabled = true, + })); + } + + [Fact] + public async Task ApproveAllApprovesOrdinaryRequest() + { + var request = new PermissionRequest { Kind = "read" }; + + var decision = await PermissionHandler.ApproveAll(request, new PermissionInvocation()); + + Assert.IsType(decision); + } + + [Fact] + public async Task ApproveAllLeavesManagedRequestPendingWhenSessionFlagIsAbsent() + { + var request = new PermissionRequestRead + { + Intention = "Read managed content", + ManagedApprovalRequired = true, + Path = "/workspace/file.txt", + }; + + var decision = await PermissionHandler.ApproveAll(request, new PermissionInvocation()); + + Assert.IsType(decision); + } + + [Fact] + public async Task ApproveAllLeavesManagedKnownVariantPendingThroughBaseType() + { + PermissionRequest request = new PermissionRequestRead + { + Intention = "Read managed content", + ManagedApprovalRequired = true, + Path = "/workspace/file.txt", + }; + + var decision = await PermissionHandler.ApproveAll(request, new PermissionInvocation()); + + Assert.IsType(decision); + } + + [Fact] + public void DerivedManagedApprovalAccessorForwardsToBaseStorage() + { + var request = new PermissionRequestRead + { + Intention = "Read managed content", + ManagedApprovalRequired = true, + Path = "/workspace/file.txt", + }; + + PermissionRequest genericRequest = request; + Assert.True(genericRequest.ManagedApprovalRequired); + + genericRequest.ManagedApprovalRequired = false; + Assert.False(request.ManagedApprovalRequired); + } + + [Fact] + public async Task ApproveAllLeavesUnknownRequestPending() + { + var request = new PermissionRequest { Kind = "future-managed-kind" }; + + var decision = await PermissionHandler.ApproveAll(request, new PermissionInvocation()); + + Assert.IsType(decision); + } +} diff --git a/go/README.md b/go/README.md index 23cd100a1..bc360253f 100644 --- a/go/README.md +++ b/go/README.md @@ -55,7 +55,6 @@ func main() { } defer client.Stop() - // Create a session (OnPermissionRequest is optional; ApproveAll allows every tool) session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ Model: "gpt-5", OnPermissionRequest: copilot.PermissionHandler.ApproveAll, @@ -222,7 +221,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `Streaming` (*bool): Enable streaming delta events (nil = runtime default) - `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration - `EnableSessionStore` (\*bool): Enables the cross-session store for search and retrieval across sessions. When unset in `ModeCopilotCli`, the runtime default applies (enabled). In `ModeEmpty`, defaults to disabled. -- `OnPermissionRequest` (PermissionHandlerFunc): Optional handler called before each tool execution to approve or deny it. When nil, permission requests are emitted as events and left pending for manual resolution. Use `copilot.PermissionHandler.ApproveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. +- `OnPermissionRequest` (PermissionHandlerFunc): Optional handler called before each tool execution to approve or deny it. When nil, permission requests are emitted as events and left pending for manual resolution. `copilot.PermissionHandler.ApproveAll` approves requests when managed settings are disabled and returns an error when `EnableManagedSettings` is true. Custom handlers can inspect `RequiresManagedApproval()` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` (UserInputHandler): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `Hooks` (\*SessionHooks): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. - `Commands` ([]CommandDefinition): Slash-commands registered for this session. See [Commands](#commands) section. @@ -681,7 +680,7 @@ An `OnPermissionRequest` handler is optional when you create or resume a session ### Approve All (simplest) -Use the built-in `PermissionHandler.ApproveAll` helper to allow every tool call without any checks: +Use the built-in `PermissionHandler.ApproveAll` helper when managed settings are disabled: ```go session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ @@ -690,9 +689,11 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi }) ``` +When `EnableManagedSettings` is true for the session, `ApproveAll` returns an error. Use a custom handler for managed sessions; request-level `RequiresManagedApproval()` remains available for human-facing confirmation logic. + ### Custom Permission Handler -Provide your own `PermissionHandlerFunc` to inspect each request and apply custom logic: +Provide your own `PermissionHandlerFunc` to inspect each request and apply custom logic. Check `RequiresManagedApproval()` before any automatic approval: ```go import ( @@ -705,6 +706,10 @@ import ( session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ Model: "gpt-5", OnPermissionRequest: func(request copilot.PermissionRequest, invocation copilot.PermissionInvocation) (rpc.PermissionDecision, error) { + if request.RequiresManagedApproval() { + return &rpc.PermissionDecisionNoResult{}, nil + } + // Type-switch on the discriminated PermissionRequest variants to // access per-kind fields: if shell, ok := request.(*copilot.PermissionRequestShell); ok { diff --git a/go/client.go b/go/client.go index 0d07e21f5..2202e92a1 100644 --- a/go/client.go +++ b/go/client.go @@ -906,7 +906,12 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses // message is dispatched) so notifications for the new session id are // routed to a registered session. initializeSession := func(sessionID string) (*Session, error) { - s := newSession(sessionID, c.client, "") + s := newSession( + sessionID, + c.client, + "", + config.EnableManagedSettings != nil && *config.EnableManagedSettings, + ) s.registerTools(config.Tools) s.registerPermissionHandler(config.OnPermissionRequest) @@ -1227,7 +1232,12 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, // Create and register the session before issuing the RPC so that // events emitted by the CLI (e.g. session.start) are not dropped. - session := newSession(sessionID, c.client, "") + session := newSession( + sessionID, + c.client, + "", + config.EnableManagedSettings != nil && *config.EnableManagedSettings, + ) session.registerTools(config.Tools) session.registerPermissionHandler(config.OnPermissionRequest) diff --git a/go/permissions.go b/go/permissions.go index f86a72683..24b9cc7f1 100644 --- a/go/permissions.go +++ b/go/permissions.go @@ -1,15 +1,23 @@ package copilot import ( + "errors" + "github.com/github/copilot-sdk/go/rpc" ) // PermissionHandler provides pre-built OnPermissionRequest implementations. var PermissionHandler = struct { - // ApproveAll approves all permission requests. + // ApproveAll approves permission requests when managed settings are disabled. ApproveAll PermissionHandlerFunc }{ - ApproveAll: func(_ PermissionRequest, _ PermissionInvocation) (rpc.PermissionDecision, error) { + ApproveAll: func(request PermissionRequest, invocation PermissionInvocation) (rpc.PermissionDecision, error) { + if invocation.ManagedSettingsEnabled { + return nil, errors.New("approveAll cannot be used when managed settings are enabled") + } + if request.RequiresManagedApproval() { + return &rpc.PermissionDecisionNoResult{}, nil + } return &rpc.PermissionDecisionApproveOnce{}, nil }, } diff --git a/go/permissions_test.go b/go/permissions_test.go new file mode 100644 index 000000000..517450dbf --- /dev/null +++ b/go/permissions_test.go @@ -0,0 +1,82 @@ +package copilot_test + +import ( + "encoding/json" + "testing" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +func TestPermissionEventExposesManagedApprovalRequired(t *testing.T) { + var data copilot.PermissionRequestedData + err := json.Unmarshal([]byte(`{ + "permissionRequest": { + "kind": "read", + "intention": "Read managed content", + "path": "/workspace/file.txt", + "managedApprovalRequired": true + }, + "requestId": "permission-1" + }`), &data) + if err != nil { + t.Fatal(err) + } + + if !data.PermissionRequest.RequiresManagedApproval() { + t.Fatal("expected managed approval to be required") + } +} + +func TestApproveAllApprovesOrdinaryRequest(t *testing.T) { + decision, err := copilot.PermissionHandler.ApproveAll( + &copilot.PermissionRequestRead{}, + copilot.PermissionInvocation{SessionID: "session-1"}, + ) + if err != nil { + t.Fatal(err) + } + if _, ok := decision.(*rpc.PermissionDecisionApproveOnce); !ok { + t.Fatalf("expected PermissionDecisionApproveOnce, got %T", decision) + } +} + +func TestApproveAllRejectsManagedSettingsSession(t *testing.T) { + decision, err := copilot.PermissionHandler.ApproveAll( + &copilot.PermissionRequestRead{}, + copilot.PermissionInvocation{ + SessionID: "session-1", + ManagedSettingsEnabled: true, + }, + ) + if err == nil { + t.Fatal("expected managed settings error") + } + if decision != nil { + t.Fatalf("expected no decision, got %T", decision) + } +} + +func TestApproveAllLeavesManagedRequestPending(t *testing.T) { + decision, err := copilot.PermissionHandler.ApproveAll( + &copilot.PermissionRequestRead{ManagedApprovalRequired: ptrTo(true)}, + copilot.PermissionInvocation{SessionID: "session-1"}, + ) + if err != nil { + t.Fatal(err) + } + if _, ok := decision.(*rpc.PermissionDecisionNoResult); !ok { + t.Fatalf("expected PermissionDecisionNoResult, got %T", decision) + } +} + +func TestRawPermissionRequestWithMalformedJSONRequiresManagedApproval(t *testing.T) { + request := rpc.RawPermissionRequest{Raw: json.RawMessage(`{"managedApprovalRequired":`)} + if !request.RequiresManagedApproval() { + t.Fatal("expected malformed raw request to fail closed") + } +} + +func ptrTo[T any](value T) *T { + return &value +} diff --git a/go/rpc/permission_request_managed_approval.go b/go/rpc/permission_request_managed_approval.go new file mode 100644 index 000000000..816181f20 --- /dev/null +++ b/go/rpc/permission_request_managed_approval.go @@ -0,0 +1,81 @@ +// Copyright (c) GitHub. All rights reserved. + +package rpc + +import "encoding/json" + +func managedApprovalRequired(value *bool) bool { + return value != nil && *value +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestCustomTool) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestExtensionManagement) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestExtensionPermissionAccess) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestHook) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestMCP) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestMemory) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestRead) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestShell) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestURL) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestWrite) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + +// RequiresManagedApproval reports whether an unknown request carries managed +// approval metadata. +func (r RawPermissionRequest) RequiresManagedApproval() bool { + var metadata struct { + ManagedApprovalRequired *bool `json:"managedApprovalRequired"` + } + if json.Unmarshal(r.Raw, &metadata) != nil { + return true + } + return managedApprovalRequired(metadata.ManagedApprovalRequired) +} diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index f12b3071a..b340b84b2 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -2905,6 +2905,7 @@ func (PermissionPromptRequestWrite) Kind() PermissionPromptRequestKind { type PermissionRequest interface { permissionRequest() Kind() PermissionRequestKind + RequiresManagedApproval() bool } type RawPermissionRequest struct { @@ -2921,6 +2922,8 @@ func (r RawPermissionRequest) Kind() PermissionRequestKind { type PermissionRequestCustomTool struct { // Arguments to pass to the custom tool Args any `json:"args,omitempty"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Tool call ID that triggered this permission request ToolCallID *string `json:"toolCallId,omitempty"` // Description of what the custom tool does @@ -2938,6 +2941,8 @@ func (PermissionRequestCustomTool) Kind() PermissionRequestKind { type PermissionRequestExtensionManagement struct { // Name of the extension being managed ExtensionName *string `json:"extensionName,omitempty"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // The extension management operation (scaffold, reload) Operation string `json:"operation"` // Tool call ID that triggered this permission request @@ -2955,6 +2960,8 @@ type PermissionRequestExtensionPermissionAccess struct { Capabilities []string `json:"capabilities"` // Name of the extension requesting permission access ExtensionName string `json:"extensionName"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Tool call ID that triggered this permission request ToolCallID *string `json:"toolCallId,omitempty"` } @@ -2968,6 +2975,8 @@ func (PermissionRequestExtensionPermissionAccess) Kind() PermissionRequestKind { type PermissionRequestHook struct { // Optional message from the hook explaining why confirmation is needed HookMessage *string `json:"hookMessage,omitempty"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Arguments of the tool call being gated ToolArgs any `json:"toolArgs,omitempty"` // Tool call ID that triggered this permission request @@ -2985,6 +2994,8 @@ func (PermissionRequestHook) Kind() PermissionRequestKind { type PermissionRequestMCP struct { // Arguments to pass to the MCP tool Args any `json:"args,omitempty"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Whether this MCP tool is read-only (no side effects) ReadOnly bool `json:"readOnly"` // Name of the MCP server providing the tool @@ -3012,6 +3023,8 @@ type PermissionRequestMemory struct { Direction *PermissionRequestMemoryDirection `json:"direction,omitempty"` // The fact being stored or voted on Fact string `json:"fact"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` // Reason for the vote (vote only) Reason *string `json:"reason,omitempty"` // Topic or subject of the memory (store only) diff --git a/go/session.go b/go/session.go index 56458f27a..a04119720 100644 --- a/go/session.go +++ b/go/session.go @@ -67,6 +67,7 @@ type Session struct { toolHandlersM sync.RWMutex permissionHandler PermissionHandlerFunc permissionMux sync.RWMutex + managedSettings bool mcpAuthHandler MCPAuthHandler mcpAuthMu sync.RWMutex userInputHandler UserInputHandler @@ -365,10 +366,16 @@ func canvasResultError(err error) error { } // newSession creates a new session wrapper with the given session ID and client. -func newSession(sessionID string, client *jsonrpc2.Client, workspacePath string) *Session { +func newSession( + sessionID string, + client *jsonrpc2.Client, + workspacePath string, + managedSettings bool, +) *Session { s := &Session{ SessionID: sessionID, workspacePath: workspacePath, + managedSettings: managedSettings, client: client, clientSessionAPIs: &rpc.ClientSessionAPIHandlers{}, handlers: make([]sessionHandler, 0), @@ -1607,11 +1614,13 @@ func (s *Session) executePermissionAndRespond(requestID string, permissionReques }() invocation := PermissionInvocation{ - SessionID: s.SessionID, + SessionID: s.SessionID, + ManagedSettingsEnabled: s.managedSettings, } decision, err := handler(permissionRequest, invocation) if err != nil { + log.Printf("permission handler failed: session_id=%s request_id=%s error=%v", s.SessionID, requestID, err) s.RPC.Permissions.HandlePendingPermissionRequest(context.Background(), &rpc.PermissionDecisionRequest{ RequestID: requestID, Result: &rpc.PermissionDecisionUserNotAvailable{}, diff --git a/go/types.go b/go/types.go index d1fc34ecd..d64aa7786 100644 --- a/go/types.go +++ b/go/types.go @@ -375,7 +375,8 @@ type PermissionHandlerFunc func(request PermissionRequest, invocation Permission // PermissionInvocation provides context about a permission request type PermissionInvocation struct { - SessionID string + SessionID string + ManagedSettingsEnabled bool } // MCPAuthWwwAuthenticateParams contains parsed parameters from an MCP server's WWW-Authenticate response. diff --git a/java/README.md b/java/README.md index 7ec030f1a..1737d82d4 100644 --- a/java/README.md +++ b/java/README.md @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.9-preview.2-01-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.10-preview.2-SNAPSHOT' ``` ## Quick Start @@ -127,6 +127,29 @@ and `setExcludedTools(...)`, prefer the source-qualified filter form `DefaultAgentConfig.setExcludedTools(...)`, use `-` directly. +## Permission Handling + +`PermissionHandler.APPROVE_ALL` approves requests when managed settings are disabled. When `enableManagedSettings` is true, it completes exceptionally. Custom handlers can inspect `request.getManagedApprovalRequired()` for human-facing confirmation logic. + +When handling `PermissionRequestedEvent` directly, convert its generated event value with `PermissionRequest.fromJsonValue(event.getData().permissionRequest())` to access the typed metadata. + +Custom handlers must check managed approval before applying kind-specific automatic decisions: + +```java +import java.util.concurrent.CompletableFuture; + +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.PermissionRequestResult; + +PermissionHandler handler = (request, invocation) -> { + if (Boolean.TRUE.equals(request.getManagedApprovalRequired())) { + return CompletableFuture.completedFuture(PermissionRequestResult.noResult()); + } + + return CompletableFuture.completedFuture(PermissionRequestResult.approveOnce()); +}; +``` + ## Try it with JBang You can run the SDK without setting up a full Java project, by using [JBang](https://www.jbang.dev/). diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/src/main/java/com/github/copilot/CopilotSession.java index c484fb1b1..aacd9ee48 100644 --- a/java/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/src/main/java/com/github/copilot/CopilotSession.java @@ -185,6 +185,7 @@ public final class CopilotSession implements AutoCloseable { private final Map commandHandlers = new ConcurrentHashMap<>(); private final Map bearerTokenProviders = new ConcurrentHashMap<>(); private final AtomicReference permissionHandler = new AtomicReference<>(); + private volatile boolean managedSettingsEnabled; private final AtomicReference mcpAuthHandler = new AtomicReference<>(); private final AtomicReference userInputHandler = new AtomicReference<>(); private final AtomicReference elicitationHandler = new AtomicReference<>(); @@ -1013,6 +1014,7 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques try { var invocation = new PermissionInvocation(); invocation.setSessionId(sessionId); + invocation.setManagedSettingsEnabled(managedSettingsEnabled); handler.handle(permissionRequest, invocation).thenAccept(result -> { try { PermissionRequestResultKind kind = new PermissionRequestResultKind(result.getKind()); @@ -1028,6 +1030,7 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques LOG.log(Level.WARNING, "Error sending permission result for requestId=" + requestId, e); } }).exceptionally(ex -> { + LOG.log(Level.SEVERE, "Permission handler failed for requestId=" + requestId, ex); try { PermissionRequestResult denied = new PermissionRequestResult(); denied.setKind(PermissionRequestResultKind.DENIED_COULD_NOT_REQUEST_FROM_USER); @@ -1378,6 +1381,10 @@ void registerPermissionHandler(PermissionHandler handler) { permissionHandler.set(handler); } + void setManagedSettingsEnabled(boolean managedSettingsEnabled) { + this.managedSettingsEnabled = managedSettingsEnabled; + } + void registerMcpAuthHandler(McpAuthHandler handler) { mcpAuthHandler.set(handler); } @@ -1403,6 +1410,7 @@ CompletableFuture handlePermissionRequest(JsonNode perm PermissionRequest request = MAPPER.treeToValue(permissionRequestData, PermissionRequest.class); var invocation = new PermissionInvocation(); invocation.setSessionId(sessionId); + invocation.setManagedSettingsEnabled(managedSettingsEnabled); return handler.handle(request, invocation).exceptionally(ex -> { LOG.log(Level.SEVERE, "Permission handler threw an exception", ex); PermissionRequestResult result = new PermissionRequestResult(); diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 633f0cd27..2b877ae95 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -356,6 +356,7 @@ static void configureSession(CopilotSession session, SessionConfig config) { if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } + session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } @@ -406,6 +407,7 @@ static void configureSession(CopilotSession session, ResumeSessionConfig config) if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } + session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionHandler.java b/java/src/main/java/com/github/copilot/rpc/PermissionHandler.java index bd8e70b75..58639beda 100644 --- a/java/src/main/java/com/github/copilot/rpc/PermissionHandler.java +++ b/java/src/main/java/com/github/copilot/rpc/PermissionHandler.java @@ -17,6 +17,11 @@ * *
{@code
  * PermissionHandler handler = (request, invocation) -> {
+ * 	if (Boolean.TRUE.equals(request.getManagedApprovalRequired())) {
+ * 		// Obtain an explicit human decision before approving this request.
+ * 		return requestHumanApproval(request);
+ * 	}
+ *
  * 	// Check the permission kind
  * 	if ("dangerous-action".equals(request.getKind())) {
  * 		// Deny dangerous actions
@@ -29,6 +34,11 @@
  * 			.completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.APPROVED));
  * };
  * }
+ *

+ * Event-based permission dispatch can use + * {@link PermissionRequestResult#noResult()} to let another connected client + * answer a pending request. Legacy protocol-v2 callbacks require a decision and + * cannot abstain. * *

* A pre-built handler that approves all requests is available as @@ -43,12 +53,21 @@ public interface PermissionHandler { /** - * A pre-built handler that approves all permission requests. + * A pre-built handler that approves permission requests when managed settings + * are disabled. * * @since 1.0.11 */ - PermissionHandler APPROVE_ALL = (request, invocation) -> CompletableFuture - .completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.APPROVED)); + PermissionHandler APPROVE_ALL = (request, invocation) -> { + if (invocation.isManagedSettingsEnabled()) { + return CompletableFuture.failedFuture( + new IllegalStateException("APPROVE_ALL cannot be used when managed settings are enabled")); + } + if (Boolean.TRUE.equals(request.getManagedApprovalRequired())) { + return CompletableFuture.completedFuture(PermissionRequestResult.noResult()); + } + return CompletableFuture.completedFuture(PermissionRequestResult.approveOnce()); + }; /** * Handles a permission request from the assistant. diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java b/java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java index bda5bdde0..10988cc1b 100644 --- a/java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java +++ b/java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java @@ -16,6 +16,7 @@ public final class PermissionInvocation { private String sessionId; + private boolean managedSettingsEnabled; /** * Gets the session ID where the permission was requested. @@ -37,4 +38,25 @@ public PermissionInvocation setSessionId(String sessionId) { this.sessionId = sessionId; return this; } + + /** + * Gets whether managed settings are enabled for this session. + * + * @return whether managed settings are enabled + */ + public boolean isManagedSettingsEnabled() { + return managedSettingsEnabled; + } + + /** + * Sets whether managed settings are enabled for this session. + * + * @param managedSettingsEnabled + * whether managed settings are enabled + * @return this invocation for method chaining + */ + public PermissionInvocation setManagedSettingsEnabled(boolean managedSettingsEnabled) { + this.managedSettingsEnabled = managedSettingsEnabled; + return this; + } } diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java b/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java index 51a303feb..a3297bfb2 100644 --- a/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java @@ -4,10 +4,18 @@ package com.github.copilot.rpc; +import java.io.IOException; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; /** * Represents a permission request from the AI assistant. @@ -20,16 +28,56 @@ * @since 1.0.0 */ @JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) public class PermissionRequest { + private static final ObjectMapper MAPPER = new ObjectMapper(); + @JsonProperty("kind") private String kind; @JsonProperty("toolCallId") private String toolCallId; + @JsonProperty("managedApprovalRequired") + @JsonDeserialize(using = ManagedApprovalRequiredDeserializer.class) + private Boolean managedApprovalRequired; + private Map extensionData; + private static final class ManagedApprovalRequiredDeserializer extends JsonDeserializer { + + @Override + public Boolean deserialize(JsonParser parser, DeserializationContext context) throws IOException { + JsonToken token = parser.currentToken(); + if (token == JsonToken.VALUE_TRUE) { + return true; + } + if (token == JsonToken.VALUE_FALSE) { + return false; + } + parser.skipChildren(); + return true; + } + } + + /** + * Converts the value exposed by a {@code permission.requested} event into a + * typed permission request. + * + * @param value + * the event's {@code permissionRequest} value + * @return the typed permission request + * @throws IllegalArgumentException + * if the value cannot be converted + */ + public static PermissionRequest fromJsonValue(Object value) { + if (value instanceof PermissionRequest request) { + return request; + } + return MAPPER.convertValue(value, PermissionRequest.class); + } + /** * Gets the kind of permission being requested. * @@ -68,6 +116,26 @@ public void setToolCallId(String toolCallId) { this.toolCallId = toolCallId; } + /** + * Gets whether managed policy requires an explicit human decision. + * + * @return {@code true} when automatic approval must be bypassed, otherwise + * {@code false} or {@code null} + */ + public Boolean getManagedApprovalRequired() { + return managedApprovalRequired; + } + + /** + * Sets whether managed policy requires an explicit human decision. + * + * @param managedApprovalRequired + * whether managed approval is required + */ + public void setManagedApprovalRequired(Boolean managedApprovalRequired) { + this.managedApprovalRequired = managedApprovalRequired; + } + /** * Gets additional extension data for the request. * diff --git a/java/src/test/java/com/github/copilot/PermissionRequestResultTest.java b/java/src/test/java/com/github/copilot/PermissionRequestResultTest.java index 4a1ff0313..c1ca9191b 100644 --- a/java/src/test/java/com/github/copilot/PermissionRequestResultTest.java +++ b/java/src/test/java/com/github/copilot/PermissionRequestResultTest.java @@ -8,7 +8,11 @@ import org.junit.jupiter.api.Test; +import com.github.copilot.generated.PermissionRequestedEvent; import com.github.copilot.rpc.PermissionRequestResult; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.PermissionInvocation; +import com.github.copilot.rpc.PermissionRequest; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.annotation.JsonInclude; @@ -70,4 +74,92 @@ void testFeedbackNotSerializedWhenNull() throws Exception { var json = MAPPER.writeValueAsString(result); assertFalse(json.contains("feedback")); } + + @Test + void testPermissionRequestExposesManagedApprovalRequired() throws Exception { + var request = MAPPER.readValue(""" + { + "kind": "read", + "path": "/workspace/file.txt", + "managedApprovalRequired": true + } + """, PermissionRequest.class); + + assertTrue(request.getManagedApprovalRequired()); + } + + @Test + void testMalformedManagedApprovalRequiredFailsClosed() throws Exception { + var request = MAPPER.readValue(""" + { + "kind": "read", + "managedApprovalRequired": 0 + } + """, PermissionRequest.class); + + assertTrue(request.getManagedApprovalRequired()); + var result = PermissionHandler.APPROVE_ALL.handle(request, new PermissionInvocation()).join(); + assertEquals("no-result", result.getKind()); + } + + @Test + void testManagedApprovalRequiredPreservesFalse() throws Exception { + var request = MAPPER.readValue(""" + { + "kind": "read", + "managedApprovalRequired": false + } + """, PermissionRequest.class); + + assertFalse(request.getManagedApprovalRequired()); + var result = PermissionHandler.APPROVE_ALL.handle(request, new PermissionInvocation()).join(); + assertEquals("approve-once", result.getKind()); + } + + @Test + void testPermissionEventValueConvertsToTypedRequest() { + var event = MAPPER + .convertValue( + java.util.Map.of("type", "permission.requested", "data", + java.util.Map.of("requestId", "permission-1", "permissionRequest", java.util.Map.of( + "kind", "url", "managedApprovalRequired", true, "url", "https://example.com"))), + PermissionRequestedEvent.class); + var request = PermissionRequest.fromJsonValue(event.getData().permissionRequest()); + + assertTrue(request.getManagedApprovalRequired()); + } + + @Test + void testApproveAllFailsWhenManagedSettingsEnabled() { + var request = new PermissionRequest(); + request.setKind("read"); + request.setManagedApprovalRequired(true); + + var invocation = new PermissionInvocation().setManagedSettingsEnabled(true); + var error = assertThrows(java.util.concurrent.CompletionException.class, + () -> PermissionHandler.APPROVE_ALL.handle(request, invocation).join()); + + assertTrue(error.getCause() instanceof IllegalStateException); + } + + @Test + void testApproveAllApprovesOrdinaryRequest() { + var request = new PermissionRequest(); + request.setKind("read"); + + var result = PermissionHandler.APPROVE_ALL.handle(request, new PermissionInvocation()).join(); + + assertEquals("approve-once", result.getKind()); + } + + @Test + void testApproveAllLeavesManagedRequestPendingWhenSessionFlagIsAbsent() { + var request = new PermissionRequest(); + request.setKind("read"); + request.setManagedApprovalRequired(true); + + var result = PermissionHandler.APPROVE_ALL.handle(request, new PermissionInvocation()).join(); + + assertEquals("no-result", result.getKind()); + } } diff --git a/nodejs/README.md b/nodejs/README.md index aef9c4683..4c430da05 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -36,7 +36,7 @@ import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); -// Create a session (onPermissionRequest is optional; approveAll allows every tool) +// approveAll is only valid when managed settings are disabled. const session = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll, @@ -137,7 +137,7 @@ Create a new conversation session. - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below) - `enableSessionStore?: boolean` - Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section. -- `onPermissionRequest?: PermissionHandler` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. Use `approveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. +- `onPermissionRequest?: PermissionHandler` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `approveAll` approves requests when managed settings are disabled and throws when `enableManagedSettings` is true. Custom handlers can inspect `managedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `onElicitationRequest?: ElicitationHandler` - Handler for elicitation requests dispatched by the server. Enables this client to present form-based UI dialogs on behalf of the agent or other session participants. See [Elicitation Requests](#elicitation-requests) section. - `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. @@ -862,7 +862,7 @@ An `onPermissionRequest` handler is optional when you create or resume a session ### Approve All (simplest) -Use the built-in `approveAll` helper to allow every tool call without any checks: +Use the built-in `approveAll` helper when managed settings are disabled: ```typescript import { CopilotClient, approveAll } from "@github/copilot-sdk"; @@ -873,9 +873,11 @@ const session = await client.createSession({ }); ``` +When `enableManagedSettings` is true for the session, `approveAll` throws. Use a custom handler for managed sessions; request-level `managedApprovalRequired` remains available for human-facing confirmation logic. + ### Custom Permission Handler -Provide your own function to inspect each request and apply custom logic: +Provide your own function to inspect each request and apply custom logic. Check `managedApprovalRequired` before any automatic approval: ```typescript import type { PermissionRequest, PermissionRequestResult } from "@github/copilot-sdk"; @@ -883,6 +885,11 @@ import type { PermissionRequest, PermissionRequestResult } from "@github/copilot const session = await client.createSession({ model: "gpt-5", onPermissionRequest: (request: PermissionRequest, invocation): PermissionRequestResult => { + if ("managedApprovalRequired" in request && request.managedApprovalRequired === true) { + // Leave the request pending for the host's human-facing confirmation flow. + return { kind: "no-result" }; + } + // request.kind — what type of operation is being requested: // "shell" — executing a shell command // "write" — writing or editing a file @@ -920,7 +927,7 @@ The handler must return one of the `PermissionDecision` shapes (or `{ kind: "no- | `"approve-permanently"` | Allow this request and persist the approval across sessions (currently used for URL domains) | `domain` (URL domain to approve) | | `"reject"` | Deny the request | `feedback?` (optional string surfaced to the agent) | | `"user-not-available"` | Deny the request because no user is available to confirm it | — | -| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | — | +| `"no-result"` | Suppress this SDK client's response so another connected client can answer the pending request | — | ### Resuming Sessions diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 792ecf31f..53821c403 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1460,7 +1460,10 @@ export class CopilotClient { this.connection!, undefined, this.onGetTraceContext, - { mcpAuthHandler: config.onMcpAuthRequest } + { + mcpAuthHandler: config.onMcpAuthRequest, + managedSettingsEnabled: config.enableManagedSettings, + } ); s.registerTools(config.tools); s.registerCanvases(config.canvases); @@ -1691,7 +1694,10 @@ export class CopilotClient { this.connection!, undefined, this.onGetTraceContext, - { mcpAuthHandler: config.onMcpAuthRequest } + { + mcpAuthHandler: config.onMcpAuthRequest, + managedSettingsEnabled: config.enableManagedSettings, + } ); session.registerTools(config.tools); session.registerCanvases(config.canvases); diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 99df267b5..43d90b2f3 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -41,13 +41,14 @@ export { // consumers can import them directly from "@github/copilot-sdk" instead of // reaching into the package's internal dist layout. See issue #1156. // -// Three names from this file are also explicitly exported elsewhere in this +// Five names from this file are also explicitly exported elsewhere in this // module — `SessionEvent` (re-exported below from `./types.js`), -// `PermissionRequest` (re-exported below from `./types.js`), and -// `AssistantMessageEvent` (re-exported above from `./session.js`). Per the -// ECMAScript module spec, the explicit named re-exports shadow the names -// arriving via `export type *`, so the hand-authored public API surface for -// those three identifiers is preserved unchanged. +// `PermissionRequest` (re-exported below from `./types.js`), +// `PermissionRequestedData`/`PermissionRequestedEvent` (also re-exported below +// from `./types.js`), and `AssistantMessageEvent` (re-exported above from +// `./session.js`). Per the ECMAScript module spec, the explicit named re-exports +// shadow the names arriving via `export type *`, so the hand-authored public API +// surface for those five identifiers is preserved unchanged. export type * from "./generated/session-events.js"; export type { CommandContext, @@ -113,6 +114,8 @@ export type { NamedProviderConfig, PermissionHandler, PermissionRequest, + PermissionRequestedData, + PermissionRequestedEvent, PermissionRequestResult, ProviderConfig, ProviderModelConfig, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 8b946c786..0d1d90fbb 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -423,6 +423,7 @@ export class CopilotSession { private transformCallbacks?: Map; private _rpc: ReturnType | null = null; private traceContextProvider?: TraceContextProvider; + private readonly managedSettingsEnabled: boolean; private _capabilities: SessionCapabilities = {}; private openCanvasInstances: OpenCanvasInstance[] = []; private disconnected = false; @@ -591,10 +592,11 @@ export class CopilotSession { private connection: MessageConnection, private _workspacePath?: string, traceContextProvider?: TraceContextProvider, - options?: { mcpAuthHandler?: McpAuthHandler } + options?: { mcpAuthHandler?: McpAuthHandler; managedSettingsEnabled?: boolean } ) { this.traceContextProvider = traceContextProvider; this.mcpAuthHandler = options?.mcpAuthHandler; + this.managedSettingsEnabled = options?.managedSettingsEnabled === true; } /** @@ -1108,6 +1110,7 @@ export class CopilotSession { try { const result = await this.permissionHandler!(permissionRequest, { sessionId: this.sessionId, + managedSettingsEnabled: this.managedSettingsEnabled, }); if (result.kind === "no-result") { return; @@ -1116,10 +1119,15 @@ export class CopilotSession { return; } await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result }); - } catch (_error) { + } catch (error) { if (this.disconnected) { return; } + console.error("Permission handler or response delivery failed", { + sessionId: this.sessionId, + requestId, + error, + }); try { await this.rpc.permissions.handlePendingPermissionRequest({ requestId, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 3da5e3bc4..ea70ae30f 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -11,6 +11,9 @@ import type { Canvas } from "./canvas.js"; import type { SessionFsProvider } from "./sessionFsProvider.js"; import type { CopilotRequestHandler } from "./copilotRequestHandler.js"; import type { + PermissionRequest as GeneratedPermissionRequest, + PermissionRequestedData as GeneratedPermissionRequestedData, + PermissionRequestedEvent as GeneratedPermissionRequestedEvent, ReasoningSummary, SessionLimitsConfig, SessionEvent as GeneratedSessionEvent, @@ -35,7 +38,9 @@ export type { ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, } from "./generated/rpc.js"; -export type SessionEvent = GeneratedSessionEvent; +export type SessionEvent = + | Exclude + | PermissionRequestedEvent; export type { ReasoningSummary } from "./generated/session-events.js"; export type { SessionFsProvider } from "./sessionFsProvider.js"; export { createSessionFsAdapter } from "./sessionFsProvider.js"; @@ -1095,16 +1100,33 @@ export type SystemMessageConfig = | SystemMessageReplaceConfig | SystemMessageCustomizeConfig; +import type { PermissionDecisionRequest } from "./generated/rpc.js"; + /** * Permission request types from the server. This is the generated * discriminated union from the runtime schema — switch on `kind` to * access the variant-specific fields (e.g. shell `commands`, write * `fileName`/`diff`, mcp `toolName`/`args`). + * + * `managedApprovalRequired` indicates that managed policy requires an explicit + * user decision. Hosts should bypass automatic approval and present their + * normal confirmation UI. The runtime currently emits it for managed Shell, + * Read, Edit, and Domain selector asks. */ -export type { PermissionRequest } from "./generated/session-events.js"; -import type { PermissionRequest } from "./generated/session-events.js"; +export type PermissionRequest = GeneratedPermissionRequest & { + readonly managedApprovalRequired?: boolean; +}; -import type { PermissionDecisionRequest } from "./generated/rpc.js"; +export type PermissionRequestedData = Omit< + GeneratedPermissionRequestedData, + "permissionRequest" +> & { + permissionRequest: PermissionRequest; +}; + +export type PermissionRequestedEvent = Omit & { + data: PermissionRequestedData; +}; /** * Permission decision result returned from a {@link PermissionHandler}. @@ -1116,10 +1138,24 @@ export type PermissionRequestResult = PermissionDecisionRequest["result"] | { ki export type PermissionHandler = ( request: PermissionRequest, - invocation: { sessionId: string } + invocation: { sessionId: string; managedSettingsEnabled?: boolean } ) => Promise | PermissionRequestResult; -export const approveAll: PermissionHandler = () => ({ kind: "approve-once" }); +/** + * Approves permission requests when managed settings are disabled. + */ +export const approveAll: PermissionHandler = (request, invocation) => { + if (invocation.managedSettingsEnabled) { + throw new Error("approveAll cannot be used when managed settings are enabled"); + } + if ("managedApprovalRequired" in request) { + const managedApprovalRequired = request.managedApprovalRequired; + if (managedApprovalRequired !== undefined && managedApprovalRequired !== false) { + return { kind: "no-result" }; + } + } + return { kind: "approve-once" }; +}; export const defaultJoinSessionPermissionHandler: PermissionHandler = (): PermissionRequestResult => ({ diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 6371642e2..e4b23deb9 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -19,6 +19,40 @@ async function stopClient(client: CopilotClient): Promise { await client.stop(); } +describe("approveAll", () => { + const request = { + kind: "url" as const, + url: "https://api.example.com/data", + intention: "Fetch domain data", + }; + const invocation = { sessionId: "session-1", managedSettingsEnabled: false }; + + it("approves ordinary permission requests", () => { + expect(approveAll(request, invocation)).toEqual({ kind: "approve-once" }); + }); + + it("rejects managed settings sessions", () => { + expect(() => approveAll(request, { ...invocation, managedSettingsEnabled: true })).toThrow( + "approveAll cannot be used when managed settings are enabled" + ); + }); + + it("leaves managed requests pending when managed settings are disabled", () => { + expect(approveAll({ ...request, managedApprovalRequired: true }, invocation)).toEqual({ + kind: "no-result", + }); + }); + + it("fails closed when managed approval metadata is malformed", () => { + const malformedRequest = { + ...request, + managedApprovalRequired: "yes", + } as unknown as Parameters[0]; + + expect(approveAll(malformedRequest, invocation)).toEqual({ kind: "no-result" }); + }); +}); + describe("CopilotClient", () => { it("disposes the stdio connection when child stdin emits an error", async () => { const client = new CopilotClient(); diff --git a/nodejs/test/session-event-types.test.ts b/nodejs/test/session-event-types.test.ts index f20c2db33..fef7acdb2 100644 --- a/nodejs/test/session-event-types.test.ts +++ b/nodejs/test/session-event-types.test.ts @@ -15,9 +15,13 @@ */ import { describe, expect, it } from "vitest"; +import { approveAll } from "../src/index.js"; import type { // The aggregate union; must still resolve via the package root. SessionEvent, + PermissionRequest, + PermissionRequestedData, + PermissionRequestedEvent, // *Data payload types from the v0.3.0 generated session-event schema. AssistantMessageData, @@ -94,6 +98,11 @@ const _defaultFactoryResultCheck: _DefaultFactoryResultIsJsonValueOrVoid = true; type _FactoryArgsRejectUndefined = FactoryContext; // @ts-expect-error Factory results must be JSON values or top-level void. type _FactoryResultRejectsFunction = FactoryDefinition void>; +type _PermissionRequestedEventStaysAlignedWithSessionEventUnion = _AssertEqual< + PermissionRequestedEvent, + Extract +>; +const _permissionRequestedEventAlignmentCheck: _PermissionRequestedEventStaysAlignedWithSessionEventUnion = true; describe("Session event type exports (#1156)", () => { it("exposes the headline ToolExecutionStartData type with a usable shape", () => { @@ -117,6 +126,82 @@ describe("Session event type exports (#1156)", () => { expect(data.turnId).toBe("turn-1"); }); + it("exposes explicit user approval metadata for managed Domain requests", () => { + const request: PermissionRequest = { + kind: "url", + url: "https://api.example.com/data", + intention: "Fetch domain data", + managedApprovalRequired: true, + }; + + expect(request.managedApprovalRequired).toBe(true); + }); + + it("exposes managed approval metadata through permission event types", () => { + const data: PermissionRequestedData = { + permissionRequest: { + kind: "url", + url: "https://api.example.com/data", + intention: "Fetch domain data", + managedApprovalRequired: true, + }, + requestId: "permission-1", + }; + const event: SessionEvent = { + id: "evt-permission-1", + parentId: null, + timestamp: "2026-01-01T00:00:00.000Z", + type: "permission.requested", + data, + }; + + if (event.type !== "permission.requested") { + throw new Error("expected permission.requested narrowing"); + } + + const permissionEvent: PermissionRequestedEvent = event; + expect(permissionEvent.data.permissionRequest.managedApprovalRequired).toBe(true); + }); + + it("rejects approveAll in managed settings sessions", () => { + expect(() => + approveAll( + { + kind: "url", + url: "https://api.example.com/data", + intention: "Fetch ordinary data", + }, + { sessionId: "session-1", managedSettingsEnabled: true } + ) + ).toThrow("approveAll cannot be used when managed settings are enabled"); + + expect(() => + approveAll( + { + kind: "url", + url: "https://api.example.com/data", + intention: "Fetch managed data", + managedApprovalRequired: true, + }, + { sessionId: "session-1", managedSettingsEnabled: true } + ) + ).toThrow("approveAll cannot be used when managed settings are enabled"); + }); + + it("leaves managed requests pending when managed settings are disabled", () => { + expect( + approveAll( + { + kind: "url", + url: "https://api.example.com/data", + intention: "Fetch managed data", + managedApprovalRequired: true, + }, + { sessionId: "session-1", managedSettingsEnabled: false } + ) + ).toEqual({ kind: "no-result" }); + }); + it("wraps ToolExecutionStartData inside the exported ToolExecutionStartEvent", () => { const event: ToolExecutionStartEvent = { id: "evt-1", @@ -174,6 +259,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); assertImportable(); assertImportable(); @@ -183,6 +269,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); // Supporting auxiliary types referenced by the *Data shapes — these // must round-trip through the package root too, otherwise consumers diff --git a/python/README.md b/python/README.md index cd8ca43c4..0206ad49b 100644 --- a/python/README.md +++ b/python/README.md @@ -121,7 +121,7 @@ async def main(): client = CopilotClient() await client.start() - # Create a session (on_permission_request is optional; approve_all allows every tool) + # approve_all is only valid when managed settings are disabled. session = await client.create_session( on_permission_request=PermissionHandler.approve_all, model="gpt-5", @@ -280,7 +280,7 @@ These are passed as keyword arguments to `create_session()`: - `provider` (ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. - `infinite_sessions` (InfiniteSessionConfig): Automatic context compaction configuration - `enable_session_store` (bool): Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. -- `on_permission_request` (callable): Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. Use `PermissionHandler.approve_all` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. +- `on_permission_request` (callable): Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.approve_all` approves requests when managed settings are disabled and raises an error when `enable_managed_settings` is true. Custom handlers can inspect `managed_approval_required` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `on_user_input_request` (callable): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `hooks` (SessionHooks): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. - `available_tools` / `excluded_tools` / `default_agent.excluded_tools` / custom-agent `tools`: MCP tools registered from `mcp_servers` are exposed to the runtime as `-`. For `available_tools` and `excluded_tools`, prefer `ToolSet().add_mcp("-")` or the raw `mcp:-` form. For custom-agent `tools` and `default_agent.excluded_tools`, use `-` directly. @@ -782,7 +782,7 @@ An `on_permission_request` handler is optional when you create or resume a sessi ### Approve All (simplest) -Use the built-in `PermissionHandler.approve_all` helper to allow every tool call without any checks: +Use the built-in `PermissionHandler.approve_all` helper to approve ordinary permission requests automatically: ```python from copilot import CopilotClient @@ -794,12 +794,14 @@ session = await client.create_session( ) ``` +When `enable_managed_settings` is true for the session, `approve_all` raises an error. Use a custom handler for managed sessions; request-level `managed_approval_required` remains available for human-facing confirmation logic. + ### Custom Permission Handler -Provide your own function to inspect each request and apply custom logic (sync or async): +Provide your own function to inspect each request and apply custom logic (sync or async). Check `managed_approval_required` before any automatic approval: ```python -from copilot import PermissionRequest, PermissionRequestResult +from copilot import PermissionNoResult, PermissionRequest, PermissionRequestResult from copilot.rpc import ( PermissionDecisionApproveOnce, PermissionDecisionReject, @@ -808,6 +810,9 @@ from copilot.session_events import PermissionRequestShell def on_permission_request(request: PermissionRequest, invocation: dict) -> PermissionRequestResult: + if getattr(request, "managed_approval_required", False) is True: + return PermissionNoResult() + # ``PermissionRequest`` is a discriminated union — pattern-match on # the variant class to access the per-kind fields. match request: @@ -830,6 +835,9 @@ Async handlers are also supported: async def on_permission_request( request: PermissionRequest, invocation: dict ) -> PermissionRequestResult: + if getattr(request, "managed_approval_required", False) is True: + return PermissionNoResult() + # Simulate an async approval check (e.g., prompting a user over a network) await asyncio.sleep(0) return PermissionDecisionApproveOnce() @@ -839,7 +847,8 @@ async def on_permission_request( The handler returns a ``PermissionRequestResult``, which is an alias for ``PermissionDecision | PermissionNoResult`` (the generated wire-level -union of every decision variant, plus a small sentinel for v1 servers). +union of every decision variant, plus a sentinel that suppresses this SDK +client's response). Approval decisions are present-tense — they describe the decision to apply, not the past-tense outcome reported back on `permission.completed` session events. @@ -849,7 +858,7 @@ session events. | `PermissionDecisionApproveOnce()` | Allow this single request | | `PermissionDecisionReject(feedback="…")` | Deny the request (optional feedback string forwarded to the LLM) | | `PermissionDecisionUserNotAvailable()` | Deny the request because no user is available to confirm it (the default) | -| `PermissionNoResult()` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | +| `PermissionNoResult()` | During event-based dispatch, suppress this SDK client's response so another connected client can answer the pending request; legacy direct callbacks cannot abstain | Several richer variants (``PermissionDecisionApproveForSession``, ``PermissionDecisionApproveForLocation``, ``PermissionDecisionApprovePermanently``, diff --git a/python/copilot/client.py b/python/copilot/client.py index e8f910a93..d0aeba8b7 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2521,7 +2521,12 @@ def _initialize_session(sid: str) -> CopilotSession: to a registered session. """ setup_start = time.perf_counter() - s = CopilotSession(sid, self._client, workspace_path=None) + s = CopilotSession( + sid, + self._client, + workspace_path=None, + managed_settings_enabled=enable_managed_settings is True, + ) if self._session_fs_config: if create_session_fs_handler is None: raise ValueError( @@ -3143,7 +3148,12 @@ async def resume_session( # Create and register the session before issuing the RPC so that # events emitted by the CLI (e.g. session.start) are not dropped. setup_start = time.perf_counter() - session = CopilotSession(session_id, self._client, workspace_path=None) + session = CopilotSession( + session_id, + self._client, + workspace_path=None, + managed_settings_enabled=enable_managed_settings is True, + ) if self._session_fs_config: if create_session_fs_handler is None: raise ValueError( diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 8099ecd59..c137c4798 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -4992,6 +4992,7 @@ class PermissionRequestCustomTool: tool_name: str args: Any = None tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestCustomTool": @@ -5000,11 +5001,13 @@ def from_dict(obj: Any) -> "PermissionRequestCustomTool": tool_name = from_str(obj.get("toolName")) args = obj.get("args") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestCustomTool( tool_description=tool_description, tool_name=tool_name, args=args, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5016,6 +5019,8 @@ def to_dict(self) -> dict: result["args"] = self.args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result @@ -5026,6 +5031,7 @@ class PermissionRequestExtensionManagement: operation: str extension_name: str | None = None tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestExtensionManagement": @@ -5033,10 +5039,12 @@ def from_dict(obj: Any) -> "PermissionRequestExtensionManagement": operation = from_str(obj.get("operation")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestExtensionManagement( operation=operation, extension_name=extension_name, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5047,6 +5055,8 @@ def to_dict(self) -> dict: result["extensionName"] = from_union([from_none, from_str], self.extension_name) if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result @@ -5057,6 +5067,7 @@ class PermissionRequestExtensionPermissionAccess: extension_name: str kind: ClassVar[str] = "extension-permission-access" tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestExtensionPermissionAccess": @@ -5064,10 +5075,12 @@ def from_dict(obj: Any) -> "PermissionRequestExtensionPermissionAccess": capabilities = from_list(from_str, obj.get("capabilities")) extension_name = from_str(obj.get("extensionName")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestExtensionPermissionAccess( capabilities=capabilities, extension_name=extension_name, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5077,6 +5090,8 @@ def to_dict(self) -> dict: result["kind"] = self.kind if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result @@ -5088,6 +5103,7 @@ class PermissionRequestHook: hook_message: str | None = None tool_args: Any = None tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestHook": @@ -5096,11 +5112,13 @@ def from_dict(obj: Any) -> "PermissionRequestHook": hook_message = from_union([from_none, from_str], obj.get("hookMessage")) tool_args = obj.get("toolArgs") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestHook( tool_name=tool_name, hook_message=hook_message, tool_args=tool_args, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5113,6 +5131,8 @@ def to_dict(self) -> dict: result["toolArgs"] = self.tool_args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result @@ -5126,6 +5146,7 @@ class PermissionRequestMcp: tool_title: str args: Any = None tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestMcp": @@ -5136,6 +5157,7 @@ def from_dict(obj: Any) -> "PermissionRequestMcp": tool_title = from_str(obj.get("toolTitle")) args = obj.get("args") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestMcp( read_only=read_only, server_name=server_name, @@ -5143,6 +5165,7 @@ def from_dict(obj: Any) -> "PermissionRequestMcp": tool_title=tool_title, args=args, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5156,6 +5179,8 @@ def to_dict(self) -> dict: result["args"] = self.args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result @@ -5170,6 +5195,7 @@ class PermissionRequestMemory: reason: str | None = None subject: str | None = None tool_call_id: str | None = None + managed_approval_required: bool | None = None @staticmethod def from_dict(obj: Any) -> "PermissionRequestMemory": @@ -5181,6 +5207,7 @@ def from_dict(obj: Any) -> "PermissionRequestMemory": reason = from_union([from_none, from_str], obj.get("reason")) subject = from_union([from_none, from_str], obj.get("subject")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) return PermissionRequestMemory( fact=fact, action=action, @@ -5189,6 +5216,7 @@ def from_dict(obj: Any) -> "PermissionRequestMemory": reason=reason, subject=subject, tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, ) def to_dict(self) -> dict: @@ -5207,6 +5235,8 @@ def to_dict(self) -> dict: result["subject"] = from_union([from_none, from_str], self.subject) if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) return result diff --git a/python/copilot/session.py b/python/copilot/session.py index c5e13579d..72b055e67 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -347,12 +347,11 @@ class SystemMessageCustomizeConfig(TypedDict, total=False): @dataclass class PermissionNoResult: - """Sentinel returned by a permission handler to leave the request unanswered. + """Sentinel that leaves an event-dispatched permission request unanswered. - Only meaningful against protocol-v1 servers. v2 servers reject ``no-result`` - responses; the SDK raises :class:`ValueError` if a v2 server receives one. - Mirrors the ``{kind: "no-result"}`` extension TS adds to its ``PermissionDecision`` - union (see ``nodejs/src/types.ts:883``). + During event-based permission dispatch, the SDK suppresses its response so + another connected client, such as a human-facing host, can answer the pending + request. Legacy direct callbacks require a concrete decision and cannot abstain. """ kind: Literal["no-result"] = "no-result" @@ -360,15 +359,21 @@ class PermissionNoResult: # The decision returned by a permission handler. Identical shape to the wire # ``PermissionDecision`` discriminated union, plus a :class:`PermissionNoResult` -# sentinel for v1 servers. Construct via the generated variant classes: +# sentinel that suppresses this SDK client's response. Construct via the +# generated variant classes: # ``PermissionDecisionApproveOnce()``, ``PermissionDecisionReject(feedback=...)``, # etc. The ``kind`` discriminator is baked in as a ``ClassVar`` default by # codegen, so callers must not pass it. PermissionRequestResult = PermissionDecision | PermissionNoResult +class PermissionInvocation(TypedDict, total=False): + session_id: Required[str] + managed_settings_enabled: NotRequired[bool] + + _PermissionHandlerFn = Callable[ - [PermissionRequest, dict[str, str]], + [PermissionRequest, PermissionInvocation], PermissionRequestResult | Awaitable[PermissionRequestResult], ] @@ -376,8 +381,12 @@ class PermissionNoResult: class PermissionHandler: @staticmethod def approve_all( - request: PermissionRequest, invocation: dict[str, str] + request: PermissionRequest, invocation: PermissionInvocation ) -> PermissionRequestResult: + if invocation.get("managed_settings_enabled", False): + raise RuntimeError("approve_all cannot be used when managed settings are enabled") + if getattr(request, "managed_approval_required", False) is True: + return PermissionNoResult() return PermissionDecisionApproveOnce() @@ -1454,7 +1463,11 @@ class CopilotSession: """ def __init__( - self, session_id: str, client: Any, workspace_path: os.PathLike[str] | str | None = None + self, + session_id: str, + client: Any, + workspace_path: os.PathLike[str] | str | None = None, + managed_settings_enabled: bool = False, ): """ Initialize a new CopilotSession. @@ -1468,8 +1481,11 @@ def __init__( client: The internal client connection to the Copilot CLI. workspace_path: Path to the session workspace directory (when infinite sessions enabled). + managed_settings_enabled: Whether managed settings were enabled when + creating or resuming the session. """ self.session_id = session_id + self._managed_settings_enabled = managed_settings_enabled self._client = client self._workspace_path = os.fsdecode(workspace_path) if workspace_path is not None else None self._event_handlers: set[Callable[[SessionEvent], None]] = set() @@ -2101,7 +2117,13 @@ async def _execute_permission_and_respond( """Execute a permission handler and respond via RPC.""" try: handler_start = time.perf_counter() - result = handler(permission_request, {"session_id": self.session_id}) + result = handler( + permission_request, + { + "session_id": self.session_id, + "managed_settings_enabled": self._managed_settings_enabled, + }, + ) if inspect.isawaitable(result): result = await result log_timing( @@ -2133,6 +2155,10 @@ async def _execute_permission_and_respond( request_id=request_id, ) except Exception: + logger.exception( + "Permission handler or response delivery failed", + extra={"session_id": self.session_id, "request_id": request_id}, + ) try: await self.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( @@ -2527,7 +2553,13 @@ async def _handle_permission_request( try: handler_start = time.perf_counter() - result = handler(request, {"session_id": self.session_id}) + result = handler( + request, + { + "session_id": self.session_id, + "managed_settings_enabled": self._managed_settings_enabled, + }, + ) if inspect.isawaitable(result): result = await result log_timing( @@ -2537,11 +2569,14 @@ async def _handle_permission_request( handler_start, session_id=self.session_id, ) - return cast(PermissionRequestResult, result) + result = cast(PermissionRequestResult, result) + if isinstance(result, PermissionNoResult): + return PermissionDecisionUserNotAvailable() + return result except Exception: # pylint: disable=broad-except # Handler failed, deny permission. - logger.debug( - "Error handling permission request", + logger.error( + "Permission handler failed", extra={"session_id": self.session_id}, exc_info=True, ) diff --git a/python/test_managed_permissions.py b/python/test_managed_permissions.py new file mode 100644 index 000000000..ca07556da --- /dev/null +++ b/python/test_managed_permissions.py @@ -0,0 +1,121 @@ +import pytest + +from copilot.rpc import PermissionDecisionApproveOnce, PermissionDecisionUserNotAvailable +from copilot.session import CopilotSession, PermissionHandler, PermissionNoResult +from copilot.session_events import ( + PermissionRequestCustomTool, + PermissionRequestedData, + PermissionRequestRead, +) + + +def test_permission_event_exposes_managed_approval_required() -> None: + data = PermissionRequestedData.from_dict( + { + "permissionRequest": { + "kind": "read", + "intention": "Read managed content", + "path": "/workspace/file.txt", + "managedApprovalRequired": True, + }, + "requestId": "permission-1", + } + ) + + assert data.permission_request.managed_approval_required is True + assert data.to_dict()["permissionRequest"]["managedApprovalRequired"] is True + + +def test_managed_metadata_preserves_existing_positional_constructor_order() -> None: + request = PermissionRequestCustomTool( + "Run a custom tool", + "custom_tool", + {"value": 1}, + "tool-call-1", + ) + + assert request.tool_call_id == "tool-call-1" + assert request.managed_approval_required is None + + read_request = PermissionRequestRead( + "Read content", + "/workspace/file.txt", + True, + False, + "Use the sandbox", + "tool-call-2", + ) + + assert read_request.managed_approval_required is True + assert read_request.request_sandbox_bypass is False + assert read_request.request_sandbox_bypass_reason == "Use the sandbox" + assert read_request.tool_call_id == "tool-call-2" + + +def test_approve_all_rejects_managed_settings_session() -> None: + request = PermissionRequestRead( + intention="Read ordinary content", + path="/workspace/file.txt", + ) + + with pytest.raises(RuntimeError, match="managed settings are enabled"): + PermissionHandler.approve_all( + request, + {"session_id": "session-1", "managed_settings_enabled": True}, + ) + + +def test_approve_all_rejects_managed_request_in_managed_settings_session() -> None: + request = PermissionRequestRead( + intention="Read managed content", + path="/workspace/file.txt", + managed_approval_required=True, + ) + + with pytest.raises(RuntimeError, match="managed settings are enabled"): + PermissionHandler.approve_all( + request, + {"session_id": "session-1", "managed_settings_enabled": True}, + ) + + +def test_approve_all_approves_ordinary_request() -> None: + request = PermissionRequestRead( + intention="Read ordinary content", + path="/workspace/file.txt", + ) + + assert isinstance( + PermissionHandler.approve_all( + request, + {"session_id": "session-1", "managed_settings_enabled": False}, + ), + PermissionDecisionApproveOnce, + ) + + +def test_approve_all_leaves_managed_request_pending_when_session_flag_is_absent() -> None: + request = PermissionRequestRead( + intention="Read managed content", + path="/workspace/file.txt", + managed_approval_required=True, + ) + + assert isinstance( + PermissionHandler.approve_all(request, {"session_id": "session-1"}), + PermissionNoResult, + ) + + +async def test_legacy_permission_callback_rejects_no_result() -> None: + request = PermissionRequestRead( + intention="Read managed content", + path="/workspace/file.txt", + managed_approval_required=True, + ) + session = CopilotSession("session-1", client=None) + session._register_permission_handler(lambda _request, _invocation: PermissionNoResult()) + + result = await session._handle_permission_request(request) + + assert isinstance(result, PermissionDecisionUserNotAvailable) diff --git a/rust/README.md b/rust/README.md index 6f68dde1e..eccc29aa2 100644 --- a/rust/README.md +++ b/rust/README.md @@ -230,6 +230,10 @@ impl PermissionHandler for MyPermissions { _rid: RequestId, data: PermissionRequestData, ) -> PermissionResult { + if data.managed_approval_required == Some(true) { + return PermissionResult::no_result(); + } + if data.extra.get("tool").and_then(|v| v.as_str()) == Some("view") { PermissionResult::approve_once() } else { @@ -250,7 +254,7 @@ let config = SessionConfig::default() .with_user_input_handler(h); ``` -The built-in `ApproveAllHandler` and `DenyAllHandler` implement `PermissionHandler` for the common cases. To observe streamed session events (assistant messages, tool calls, etc.), call `session.subscribe()` — see [Streaming](#streaming) below. +The built-in `ApproveAllHandler` and `DenyAllHandler` implement `PermissionHandler` for the common cases. When `enable_managed_settings` is true, `ApproveAllHandler` logs an error and returns a user-not-available decision; custom handlers can inspect `managed_approval_required` when implementing a human-facing confirmation flow. To observe streamed session events (assistant messages, tool calls, etc.), call `session.subscribe()` — see [Streaming](#streaming) below. ### SessionConfig @@ -430,6 +434,8 @@ Reach for the `ToolHandler` trait directly when you need shared state across mul Set a permission policy directly on `SessionConfig` with the chainable builders. They install a synthesized `PermissionHandler` so only permission requests are intercepted; every other event flows through unchanged. +When `enable_managed_settings` is true, the approve-all policy logs an error and returns a user-not-available decision. Custom handlers can inspect `managed_approval_required` for human-facing confirmation logic. + ```rust,ignore let session = client .create_session( diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index cb3a45b21..2aef5765c 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -3102,6 +3102,9 @@ pub struct PermissionRequestMcp { pub args: Option, /// Permission kind discriminator pub kind: PermissionRequestMcpKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Whether this MCP tool is read-only (no side effects) pub read_only: bool, /// Name of the MCP server providing the tool @@ -3159,6 +3162,9 @@ pub struct PermissionRequestMemory { pub fact: String, /// Permission kind discriminator pub kind: PermissionRequestMemoryKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Reason for the vote (vote only) #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, @@ -3179,6 +3185,9 @@ pub struct PermissionRequestCustomTool { pub args: Option, /// Permission kind discriminator pub kind: PermissionRequestCustomToolKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Tool call ID that triggered this permission request #[serde(skip_serializing_if = "Option::is_none")] pub tool_call_id: Option, @@ -3197,6 +3206,9 @@ pub struct PermissionRequestHook { pub hook_message: Option, /// Permission kind discriminator pub kind: PermissionRequestHookKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Arguments of the tool call being gated #[serde(skip_serializing_if = "Option::is_none")] pub tool_args: Option, @@ -3216,6 +3228,9 @@ pub struct PermissionRequestExtensionManagement { pub extension_name: Option, /// Permission kind discriminator pub kind: PermissionRequestExtensionManagementKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// The extension management operation (scaffold, reload) pub operation: String, /// Tool call ID that triggered this permission request @@ -3233,6 +3248,9 @@ pub struct PermissionRequestExtensionPermissionAccess { pub extension_name: String, /// Permission kind discriminator pub kind: PermissionRequestExtensionPermissionAccessKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, /// Tool call ID that triggered this permission request #[serde(skip_serializing_if = "Option::is_none")] pub tool_call_id: Option, diff --git a/rust/src/handler.rs b/rust/src/handler.rs index 3287a4f09..77edf919c 100644 --- a/rust/src/handler.rs +++ b/rust/src/handler.rs @@ -83,6 +83,11 @@ impl From for PermissionResult { } } +pub(crate) fn permission_handler_failure(message: &str) -> PermissionResult { + tracing::error!(error = message, "permission handler failed"); + PermissionResult::user_not_available() +} + /// Response to a user input request. #[derive(Debug, Clone)] pub struct UserInputResponse { @@ -273,9 +278,12 @@ pub trait AutoModeSwitchHandler: Send + Sync + 'static { ) -> AutoModeSwitchResponse; } -/// A [`PermissionHandler`] that approves every request. Useful for CLI -/// tools, scripts, and tests that don't need interactive permission -/// prompts. +/// A [`PermissionHandler`] that approves ordinary requests when managed settings are disabled. +/// +/// When managed settings are enabled, the handler logs an error and returns a +/// user-not-available decision. As a defense-in-depth fallback, a request marked +/// as requiring managed approval is left unanswered even if the session flag is +/// absent. #[derive(Debug, Clone)] pub struct ApproveAllHandler; @@ -285,9 +293,17 @@ impl PermissionHandler for ApproveAllHandler { &self, _session_id: SessionId, _request_id: RequestId, - _data: PermissionRequestData, + data: PermissionRequestData, ) -> PermissionResult { - PermissionResult::approve_once() + if data.managed_settings_enabled { + permission_handler_failure( + "ApproveAllHandler cannot be used when managed settings are enabled", + ) + } else if data.managed_approval_required == Some(true) { + PermissionResult::no_result() + } else { + PermissionResult::approve_once() + } } } @@ -326,6 +342,39 @@ mod tests { )); } + #[tokio::test] + async fn approve_all_handler_fails_when_managed_settings_enabled() { + let result = ApproveAllHandler + .handle( + SessionId::from("s1"), + RequestId::new("1"), + PermissionRequestData { + managed_settings_enabled: true, + ..Default::default() + }, + ) + .await; + assert!(matches!( + result, + PermissionResult::Decision(PermissionDecision::UserNotAvailable(_)) + )); + } + + #[tokio::test] + async fn approve_all_handler_leaves_managed_approval_pending() { + let result = ApproveAllHandler + .handle( + SessionId::from("s1"), + RequestId::new("1"), + PermissionRequestData { + managed_approval_required: Some(true), + ..Default::default() + }, + ) + .await; + assert!(matches!(result, PermissionResult::NoResult)); + } + #[tokio::test] async fn deny_all_handler_returns_denied() { let result = DenyAllHandler diff --git a/rust/src/permission.rs b/rust/src/permission.rs index 2ddd773a3..e353ce315 100644 --- a/rust/src/permission.rs +++ b/rust/src/permission.rs @@ -16,10 +16,14 @@ use std::sync::Arc; use async_trait::async_trait; -use crate::handler::{PermissionHandler, PermissionResult}; +use crate::handler::{PermissionHandler, PermissionResult, permission_handler_failure}; use crate::types::{PermissionRequestData, RequestId, SessionId}; -/// Return a [`PermissionHandler`] that approves every request. +/// Return a [`PermissionHandler`] that approves requests when managed settings +/// are disabled. +/// +/// When managed settings are enabled, the handler logs an error and returns a +/// user-not-available decision. pub fn approve_all() -> Arc { Arc::new(PolicyHandler { policy: Policy::ApproveAll, @@ -116,7 +120,15 @@ impl PermissionHandler for PolicyHandler { Policy::Predicate(f) => f(&data), }; if approved { - PermissionResult::approve_once() + if matches!(self.policy, Policy::ApproveAll) && data.managed_settings_enabled { + permission_handler_failure( + "approve-all policy cannot be used when managed settings are enabled", + ) + } else if data.managed_approval_required == Some(true) { + PermissionResult::no_result() + } else { + PermissionResult::approve_once() + } } else { PermissionResult::reject(None) } @@ -144,6 +156,18 @@ mod tests { )); } + #[tokio::test] + async fn approve_all_fails_when_managed_settings_enabled() { + let h = approve_all(); + let mut request = data(); + request.managed_settings_enabled = true; + assert!(matches!( + h.handle(SessionId::from("s"), RequestId::new("1"), request) + .await, + PermissionResult::Decision(crate::types::PermissionDecision::UserNotAvailable(_)) + )); + } + #[tokio::test] async fn deny_all_denies() { let h = deny_all(); @@ -164,6 +188,30 @@ mod tests { )); } + #[tokio::test] + async fn approve_if_leaves_managed_approval_pending_when_predicate_approves() { + let h = approve_if(|_| true); + let mut request = data(); + request.managed_approval_required = Some(true); + assert!(matches!( + h.handle(SessionId::from("s"), RequestId::new("1"), request) + .await, + PermissionResult::NoResult + )); + } + + #[tokio::test] + async fn approve_if_still_rejects_managed_request_when_predicate_denies() { + let h = approve_if(|_| false); + let mut request = data(); + request.managed_approval_required = Some(true); + assert!(matches!( + h.handle(SessionId::from("s"), RequestId::new("1"), request) + .await, + PermissionResult::Decision(crate::types::PermissionDecision::Reject(_)) + )); + } + #[tokio::test] async fn resolve_handler_policy_wins() { struct AlwaysApprove; diff --git a/rust/src/session.rs b/rust/src/session.rs index adaab4c2e..6919f2813 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -57,6 +57,7 @@ const TOOL_SEARCH_TOOL_NAME: &str = "tool_search_tool"; #[derive(Clone)] pub(crate) struct SessionHandlers { pub permission: Option>, + pub managed_settings_enabled: bool, pub elicitation: Option>, pub mcp_auth: Option>, pub user_input: Option>, @@ -896,6 +897,7 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, + managed_settings_enabled: wire.enable_managed_settings == Some(true), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -1163,6 +1165,7 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, + managed_settings_enabled: wire.enable_managed_settings == Some(true), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -1524,6 +1527,35 @@ fn extract_request_id(data: &Value) -> Option { .map(RequestId::new) } +fn permission_request_data( + event_data: &Value, + managed_settings_enabled: bool, +) -> PermissionRequestData { + let request_data = event_data + .get("permissionRequest") + .cloned() + .unwrap_or_else(|| event_data.clone()); + let managed_approval_required = match request_data.get("managedApprovalRequired") { + None => None, + Some(Value::Bool(value)) => Some(*value), + Some(_) => Some(true), + }; + match serde_json::from_value::(request_data) { + Ok(mut data) => { + data.extra = event_data.clone(); + data.managed_settings_enabled = managed_settings_enabled; + data + } + Err(_) => PermissionRequestData { + kind: None, + tool_call_id: None, + managed_approval_required, + managed_settings_enabled, + extra: event_data.clone(), + }, + } +} + /// Map a [`PermissionResult`] to the `result` payload sent back to the /// server via `session.permissions.handlePendingPermissionRequest`. /// @@ -1709,14 +1741,10 @@ async fn handle_notification( }; let client = client.clone(); let sid = session_id.clone(); - let data: PermissionRequestData = - serde_json::from_value(notification.event.data.clone()).unwrap_or_else(|_| { - PermissionRequestData { - kind: None, - tool_call_id: None, - extra: notification.event.data.clone(), - } - }); + let data = permission_request_data( + ¬ification.event.data, + handlers.managed_settings_enabled, + ); let span = tracing::error_span!( "permission_request_handler", session_id = %sid, @@ -2518,7 +2546,7 @@ fn inject_transform_sections_resume( mod tests { use serde_json::json; - use super::notification_permission_payload; + use super::{notification_permission_payload, permission_request_data}; use crate::handler::PermissionResult; #[test] @@ -2545,4 +2573,77 @@ mod tests { Some(json!({ "kind": "user-not-available" })) ); } + + #[test] + fn permission_request_data_reads_nested_managed_approval_metadata() { + let data = permission_request_data( + &json!({ + "requestId": "permission-1", + "permissionRequest": { + "kind": "read", + "managedApprovalRequired": true, + "path": "/workspace/file.txt" + } + }), + false, + ); + + assert_eq!(data.managed_approval_required, Some(true)); + assert_eq!( + data.extra["permissionRequest"]["path"], + "/workspace/file.txt" + ); + } + + #[test] + fn permission_request_data_preserves_managed_flag_when_other_fields_are_malformed() { + let data = permission_request_data( + &json!({ + "requestId": "permission-1", + "permissionRequest": { + "kind": "read", + "managedApprovalRequired": true, + "toolCallId": 42 + } + }), + false, + ); + + assert_eq!(data.managed_approval_required, Some(true)); + assert_eq!(data.extra["requestId"], "permission-1"); + } + + #[test] + fn permission_request_data_fails_closed_for_malformed_managed_flag() { + let data = permission_request_data( + &json!({ + "requestId": "permission-1", + "permissionRequest": { + "kind": "read", + "managedApprovalRequired": "yes", + "path": "/workspace/file.txt" + } + }), + false, + ); + + assert_eq!(data.managed_approval_required, Some(true)); + } + + #[test] + fn permission_request_data_preserves_valid_false_managed_flag() { + let data = permission_request_data( + &json!({ + "requestId": "permission-1", + "permissionRequest": { + "kind": "read", + "managedApprovalRequired": false, + "path": "/workspace/file.txt" + } + }), + false, + ); + + assert_eq!(data.managed_approval_required, Some(false)); + } } diff --git a/rust/src/types.rs b/rust/src/types.rs index 81d86b72b..b93633f5d 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -5447,8 +5447,15 @@ pub struct PermissionRequestData { /// to a specific tool invocation. #[serde(default, skip_serializing_if = "Option::is_none")] pub tool_call_id: Option, - /// The full permission request params from the CLI. The shape varies by - /// permission type and CLI version, so we preserve it as `Value`. + /// Whether managed policy requires an explicit human decision. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, + /// Whether managed settings are enabled for this session. + #[serde(default, skip_serializing_if = "is_false")] + pub managed_settings_enabled: bool, + /// The full permission event params from the CLI, including the request ID + /// and nested permission request. The shape varies by permission type and + /// CLI version, so we preserve it as `Value`. #[serde(flatten)] pub extra: Value, } diff --git a/rust/tests/api_types_test.rs b/rust/tests/api_types_test.rs index bcf226691..9b86b1367 100644 --- a/rust/tests/api_types_test.rs +++ b/rust/tests/api_types_test.rs @@ -7,6 +7,7 @@ use github_copilot_sdk::rpc::{ Extension, ExtensionList, ExtensionSource, ExtensionStatus, ExtensionsDisableRequest, ExtensionsEnableRequest, FleetStartRequest, FleetStartResult, TasksStartAgentRequest, }; +use github_copilot_sdk::session_events::{PermissionRequest, PermissionRequestedData}; #[test] fn extension_running_has_expected_status_and_source() { @@ -84,6 +85,25 @@ fn tasks_start_agent_request_fields_are_accessible() { assert_eq!(request.description.as_deref(), Some("SDK task agent")); } +#[test] +fn permission_event_exposes_managed_approval_required() { + let data: PermissionRequestedData = serde_json::from_value(serde_json::json!({ + "permissionRequest": { + "kind": "read", + "intention": "Read managed content", + "path": "/workspace/file.txt", + "managedApprovalRequired": true + }, + "requestId": "permission-1" + })) + .unwrap(); + + let PermissionRequest::Read(request) = data.permission_request else { + panic!("expected read permission request"); + }; + assert_eq!(request.managed_approval_required, Some(true)); +} + fn running_extension(id: &str, name: &str) -> Extension { Extension { id: id.to_string(), diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 808562630..97fcebea6 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -66,6 +66,10 @@ const TYPE_RENAMES: Record = { PermissionRequestedDataPermissionRequest: "PermissionRequest", }; +const POLYMORPHIC_BASE_PROPERTIES: Record = { + PermissionRequest: ["managedApprovalRequired"], +}; + /** Apply rename to a generated class name, checking both exact match and prefix replacement for derived types. */ function applyTypeRename(className: string): string { if (TYPE_RENAMES[className]) return TYPE_RENAMES[className]; @@ -827,6 +831,7 @@ function generatePolymorphicClasses( const lines: string[] = []; const discriminatorInfo = findDiscriminator(variants)!; const renamedBase = applyTypeRename(baseClassName); + const baseProperties = new Set(POLYMORPHIC_BASE_PROPERTIES[renamedBase] ?? []); lines.push(...xmlDocCommentWithFallback(description, `Polymorphic base type discriminated by ${escapeXml(discriminatorProperty)}.`, "")); if (experimental) pushExperimentalAttribute(lines); @@ -845,13 +850,52 @@ function generatePolymorphicClasses( lines.push(` ///

The type discriminator.`); lines.push(` [JsonPropertyName("${discriminatorProperty}")]`); lines.push(` public virtual string ${toPascalCase(discriminatorProperty)} { get; set; } = string.Empty;`); + for (const propName of baseProperties) { + const propSchema = variants + .map((variant) => variant.properties?.[propName]) + .find((property): property is JSONSchema7 => typeof property === "object"); + if (!propSchema) continue; + + const csharpName = toCSharpPropertyName(propName, propSchema); + const csharpType = resolver( + propSchema, + renamedBase, + csharpName, + false, + knownTypes, + nestedClasses, + enumOutput + ); + lines.push(""); + lines.push(...xmlDocPropertyComment(propSchema.description, propName, " ")); + lines.push(...emitDataAnnotations(propSchema, " ", csharpType)); + if (isSchemaDeprecated(propSchema)) pushObsoleteAttributes(lines, " "); + if (isSchemaExperimental(propSchema)) pushExperimentalAttribute(lines, " "); + lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`); + const propVisibility = pushCSharpInternalAttribute(lines, propSchema); + lines.push(` [JsonPropertyName("${propName}")]`); + lines.push(` ${propVisibility} virtual ${csharpType} ${csharpName} { get; set; }`); + } lines.push(`}`); lines.push(""); for (const { value, schema } of discriminatorInfo.mapping.values()) { const constValue = String(value); const derivedClassName = applyTypeRename(`${baseClassName}${toPascalCase(constValue)}`); - const derivedCode = generateDerivedClass(derivedClassName, renamedBase, discriminatorProperty, constValue, schema, knownTypes, nestedClasses, enumOutput, resolver, experimental, options); + const derivedCode = generateDerivedClass( + derivedClassName, + renamedBase, + discriminatorProperty, + constValue, + schema, + knownTypes, + nestedClasses, + enumOutput, + resolver, + experimental, + options, + baseProperties + ); nestedClasses.set(derivedClassName, derivedCode); } @@ -872,7 +916,8 @@ function generateDerivedClass( enumOutput: string[], propertyResolver: PropertyTypeResolver, experimental = false, - options: DiscriminatedUnionGenerationOptions = {} + options: DiscriminatedUnionGenerationOptions = {}, + baseProperties: ReadonlySet = new Set() ): string { const lines: string[] = []; const required = new Set(schema.required || []); @@ -897,6 +942,18 @@ function generateDerivedClass( const csharpName = toCSharpPropertyName(propName, prop); const csharpType = propertyResolver(prop, className, csharpName, isReq, knownTypes, nestedClasses, enumOutput); + if (baseProperties.has(propName)) { + lines.push(` /// `); + if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`); + lines.push(` [JsonPropertyName("${propName}")]`); + lines.push(` public override ${csharpType} ${csharpName}`); + lines.push(` {`); + lines.push(` get => base.${csharpName};`); + lines.push(` set => base.${csharpName} = value;`); + lines.push(` }`, ""); + continue; + } + lines.push(...xmlDocPropertyComment(prop.description, propName, " ")); lines.push(...emitDataAnnotations(prop, " ", csharpType)); if (isSchemaDeprecated(prop)) pushObsoleteAttributes(lines, " "); diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts index b1d7474b9..d6eda7f99 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -14,6 +14,7 @@ import { fileURLToPath } from "url"; import { promisify } from "util"; import wordwrap from "wordwrap"; import { + addManagedApprovalRequiredToPermissionRequests, cloneSchemaForCodegen, collectDefinitionCollections, collectExperimentalOnlyRpcReferencedDefinitionNames, @@ -1812,6 +1813,9 @@ function emitGoFlatDiscriminatedUnion( lines.push(`type ${typeName} interface {`); lines.push(`\t${markerName}()`); lines.push(`\t${discriminatorMethodName}() ${discGoType}`); + if (typeName === "PermissionRequest") { + lines.push(`\tRequiresManagedApproval() bool`); + } lines.push(`}`); lines.push(``); @@ -3679,7 +3683,9 @@ async function generateSessionEvents(schemaPath?: string, apiSchema?: ApiSchema) console.log("Go: generating session-events..."); const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath()); - const schema = cloneSchemaForCodegen((await loadSchemaJson(resolvedPath)) as JSONSchema7); + const schema = addManagedApprovalRequiredToPermissionRequests( + (await loadSchemaJson(resolvedPath)) as JSONSchema7 + ); const processed = propagateInternalVisibility(postProcessSchema(schema)); const processedApiSchema = apiSchema ? propagateInternalVisibility(postProcessSchema(cloneSchemaForCodegen(apiSchema as JSONSchema7)) as JSONSchema7) diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index cf74efbbd..978021a98 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -11,6 +11,7 @@ import path from "path"; import type { JSONSchema7, JSONSchema7Definition } from "json-schema"; import { fileURLToPath } from "url"; import { + addManagedApprovalRequiredToPermissionRequests, cloneSchemaForCodegen, filterNodeByVisibility, fixNullableRequiredRefsInApiSchema, @@ -2341,9 +2342,19 @@ function emitPyClass( const fieldEntries = Object.entries(schema.properties || {}).filter( ([, value]) => typeof value === "object" ) as Array<[string, JSONSchema7]>; + const optionalFieldEntries = fieldEntries + .filter(([name]) => !required.has(name)) + .sort(([left, leftSchema], [right, rightSchema]) => { + const leftAppendOnly = + (leftSchema as Record)["x-copilot-sdk-append-last"] === true; + const rightAppendOnly = + (rightSchema as Record)["x-copilot-sdk-append-last"] === true; + if (leftAppendOnly !== rightAppendOnly) return leftAppendOnly ? 1 : -1; + return left.localeCompare(right); + }); const orderedFieldEntries = [ ...fieldEntries.filter(([name]) => required.has(name)).sort(([a], [b]) => a.localeCompare(b)), - ...fieldEntries.filter(([name]) => !required.has(name)).sort(([a], [b]) => a.localeCompare(b)), + ...optionalFieldEntries, ]; const fieldInfos = orderedFieldEntries.map(([propName, propSchema]) => { @@ -2930,7 +2941,9 @@ async function generateSessionEvents(schemaPath?: string): Promise { console.log("Python: generating session-events..."); const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath()); - const schema = (await loadSchemaJson(resolvedPath)) as JSONSchema7; + const schema = addManagedApprovalRequiredToPermissionRequests( + (await loadSchemaJson(resolvedPath)) as JSONSchema7 + ); const processed = propagateInternalVisibility(postProcessSchema(schema)); let code = generatePythonSessionEventsCode(processed); const { typeNames } = collectInternalSymbols(processed); diff --git a/scripts/codegen/rust.ts b/scripts/codegen/rust.ts index 127dc2e48..4090318f0 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -17,6 +17,7 @@ import { fileURLToPath } from "url"; import { promisify } from "util"; import type { JSONSchema7, JSONSchema7Definition } from "json-schema"; import { + addManagedApprovalRequiredToPermissionRequests, type ApiSchema, type DefinitionCollections, EXCLUDED_EVENT_TYPES, @@ -2211,7 +2212,9 @@ async function generate(): Promise { const sessionEventsSchema = propagateInternalVisibility( postProcessSchema( - stripBooleanLiterals(sessionEventsRaw) as JSONSchema7, + stripBooleanLiterals( + addManagedApprovalRequiredToPermissionRequests(sessionEventsRaw as JSONSchema7), + ) as JSONSchema7, ), ); const apiSchema = propagateInternalVisibility( diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index 9ab335b05..ba4c9442b 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -453,6 +453,48 @@ export function cloneSchemaForCodegen(value: T): T { return value; } +const PERMISSION_REQUEST_DEFINITION_NAMES = [ + "PermissionRequestCustomTool", + "PermissionRequestExtensionManagement", + "PermissionRequestExtensionPermissionAccess", + "PermissionRequestHook", + "PermissionRequestMcp", + "PermissionRequestMemory", + "PermissionRequestRead", + "PermissionRequestShell", + "PermissionRequestUrl", + "PermissionRequestWrite", +] as const; + +/** + * Add managed approval metadata until the pinned CLI schema includes the field. + */ +export function addManagedApprovalRequiredToPermissionRequests(schema: T): T { + const cloned = cloneSchemaForCodegen(schema); + const property: JSONSchema7 = { + description: + "When true, managed policy requires an explicit user decision and automatic approval must be bypassed.", + type: ["boolean", "null"], + }; + (property as Record)["x-copilot-sdk-append-last"] = true; + + for (const definitions of [cloned.definitions, cloned.$defs]) { + if (!definitions) continue; + for (const name of PERMISSION_REQUEST_DEFINITION_NAMES) { + const definition = definitions[name]; + if (!definition || typeof definition !== "object") continue; + const objectDefinition = definition as JSONSchema7; + objectDefinition.properties = { + ...objectDefinition.properties, + managedApprovalRequired: + objectDefinition.properties?.managedApprovalRequired ?? cloneSchemaForCodegen(property), + }; + } + } + + return cloned; +} + export function getEnumValueDescriptions(schema: JSONSchema7 | null | undefined): EnumValueDescriptions | undefined { if (!schema || typeof schema !== "object") return undefined; From b88e8c81a943a32ba7267e677a7a0bb23da178d8 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 31 Jul 2026 13:53:41 -0700 Subject: [PATCH 03/72] sdk: Expose githubMcpToolConfig across languages (#2112) * sdk: Expose githubMcpToolConfig across languages Adds the `githubMcpToolConfig` session option to `session.create` and `session.resume` in all six SDKs, forwarding the built-in GitHub MCP server settings the runtime already accepts: `enableAllTools`, `additionalToolsets`, `additionalTools`, `enableInsidersMode`, and the new `disableFormDeferral`. `disableFormDeferral` lets autonomous SDK workflows opt out of MCP App form deferral, so form-backed GitHub write tools execute directly instead of returning an awaiting-form stub. It requires MCP Apps to be enabled for the session. The option is omitted from the wire payload entirely when unset, so existing consumers see no change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * python: apply ruff formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * java: apply spotless formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback - node: omit githubMcpToolConfig for null as well as undefined - python: document github_mcp_tool_config on create_session/resume_session - go: assert json.Unmarshal succeeds in the omitted-when-unset subtest - java: call the non-deprecated buildCreateRequest(config, sessionId) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add GitHub MCP tool config E2E coverage Exercise the writable built-in GitHub MCP endpoint from Node and Go so regressions that drop githubMcpToolConfig fail end to end. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: capture GitHub MCP requests generically Replace snapshot-specific replay behavior with a reusable captured-request endpoint and assert the writable GitHub MCP route and headers from Node and Go E2E tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 8 +- dotnet/src/Types.cs | 51 ++++++ dotnet/test/Unit/SerializationTests.cs | 45 ++++++ go/client.go | 2 + go/client_test.go | 62 +++++++ go/internal/e2e/session_config_e2e_test.go | 75 +++++++++ go/internal/e2e/testharness/context.go | 5 + go/internal/e2e/testharness/proxy.go | 32 ++++ go/types.go | 22 +++ .../github/copilot/SessionRequestBuilder.java | 2 + .../copilot/rpc/CreateSessionRequest.java | 13 ++ .../copilot/rpc/GitHubMcpToolConfig.java | 82 ++++++++++ .../copilot/rpc/ResumeSessionConfig.java | 23 +++ .../copilot/rpc/ResumeSessionRequest.java | 13 ++ .../com/github/copilot/rpc/SessionConfig.java | 23 +++ .../copilot/SessionRequestBuilderTest.java | 20 +++ nodejs/src/client.ts | 6 + nodejs/src/index.ts | 1 + nodejs/src/types.ts | 22 +++ nodejs/test/client.test.ts | 49 ++++++ nodejs/test/e2e/harness/CapiProxy.ts | 6 + nodejs/test/e2e/session_config.e2e.test.ts | 38 +++++ python/copilot/__init__.py | 2 + python/copilot/client.py | 43 +++++ python/copilot/session.py | 16 ++ python/test_client.py | 40 +++++ rust/src/types.rs | 153 +++++++++++++++++- rust/src/wire.rs | 11 +- test/harness/replayingCapiProxy.ts | 15 ++ ...pply_github_mcp_tool_config_on_create.yaml | 3 + 30 files changed, 872 insertions(+), 11 deletions(-) create mode 100644 java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java create mode 100644 test/snapshots/session_config/should_apply_github_mcp_tool_config_on_create.yaml diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 595aa3356..46ce7ba80 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1200,6 +1200,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance ToolFilterPrecedence: toolFilter.ToolFilterPrecedence, ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, + GitHubMcpToolConfig: config.GitHubMcpToolConfig, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -1415,6 +1416,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ToolFilterPrecedence: toolFilter.ToolFilterPrecedence, ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, + GitHubMcpToolConfig: config.GitHubMcpToolConfig, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -2768,7 +2770,8 @@ internal record CreateSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, - bool? EnableGitHubTelemetryForwarding = null); + bool? EnableGitHubTelemetryForwarding = null, + [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); #pragma warning restore GHCP001 internal record ToolDefinition( @@ -2875,7 +2878,8 @@ internal record ResumeSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, - bool? EnableGitHubTelemetryForwarding = null); + bool? EnableGitHubTelemetryForwarding = null, + [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); #pragma warning restore GHCP001 internal record ResumeSessionResponse( diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index a5b620702..6b511117c 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -2960,6 +2960,36 @@ public sealed class CopilotExpAssignmentResponse public string AssignmentContext { get; set; } = string.Empty; } +/// +/// Configuration for the built-in GitHub MCP server. +/// +public sealed class GitHubMcpToolConfig +{ + /// Enables all GitHub MCP tools. + [JsonPropertyName("enableAllTools")] + public bool? EnableAllTools { get; set; } + + /// Additional GitHub MCP toolsets to enable. + [JsonPropertyName("additionalToolsets")] + public IList? AdditionalToolsets { get; set; } + + /// Additional GitHub MCP tools to enable. + [JsonPropertyName("additionalTools")] + public IList? AdditionalTools { get; set; } + + /// Enables GitHub MCP insiders-mode tools. + [JsonPropertyName("enableInsidersMode")] + public bool? EnableInsidersMode { get; set; } + + /// + /// Disables form deferral for GitHub MCP tools. This only applies to the + /// built-in GitHub MCP server and only has an effect when MCP Apps and + /// form-backed GitHub tools are enabled. + /// + [JsonPropertyName("disableFormDeferral")] + public bool? DisableFormDeferral { get; set; } +} + /// /// Shared configuration properties for creating or resuming a Copilot session. /// Use when creating a new session, or @@ -2997,6 +3027,20 @@ protected SessionConfigBase(SessionConfigBase? other) EnableSessionStore = other.EnableSessionStore; EnableSkills = other.EnableSkills; EnableMcpApps = other.EnableMcpApps; + GitHubMcpToolConfig = other.GitHubMcpToolConfig is null + ? null + : new GitHubMcpToolConfig + { + EnableAllTools = other.GitHubMcpToolConfig.EnableAllTools, + AdditionalToolsets = other.GitHubMcpToolConfig.AdditionalToolsets is not null + ? [.. other.GitHubMcpToolConfig.AdditionalToolsets] + : null, + AdditionalTools = other.GitHubMcpToolConfig.AdditionalTools is not null + ? [.. other.GitHubMcpToolConfig.AdditionalTools] + : null, + EnableInsidersMode = other.GitHubMcpToolConfig.EnableInsidersMode, + DisableFormDeferral = other.GitHubMcpToolConfig.DisableFormDeferral, + }; ExcludedBuiltInAgents = other.ExcludedBuiltInAgents is not null ? [.. other.ExcludedBuiltInAgents] : null; ExcludedTools = other.ExcludedTools is not null ? [.. other.ExcludedTools] : null; Hooks = other.Hooks; @@ -3305,6 +3349,13 @@ protected SessionConfigBase(SessionConfigBase? other) [Experimental(Diagnostics.Experimental)] public bool EnableMcpApps { get; set; } + /// + /// Configuration for the built-in GitHub MCP server. + /// DisableFormDeferral only applies to that server and only has an + /// effect when MCP Apps and form-backed GitHub tools are enabled. + /// + public GitHubMcpToolConfig? GitHubMcpToolConfig { get; set; } + /// Hook handlers for session lifecycle events. public SessionHooks? Hooks { get; set; } diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index 7e7551395..9108a8134 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -299,6 +299,51 @@ public void SessionRequests_OmitCapiOptions_WhenUnset() Assert.False(resumeDocument.RootElement.TryGetProperty("capi", out _)); } + [Fact] + public void SessionRequests_CanSerializeGitHubMcpToolConfig_WithSdkOptions() + { + var options = GetSerializerOptions(); + var githubConfig = new GitHubMcpToolConfig + { + EnableAllTools = true, + AdditionalToolsets = ["repos"], + AdditionalTools = ["get_issue"], + EnableInsidersMode = true, + DisableFormDeferral = true, + }; + + var createRequestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest"); + var createRequest = CreateInternalRequest( + createRequestType, + ("GitHubMcpToolConfig", githubConfig)); + using var createDocument = JsonDocument.Parse(JsonSerializer.Serialize(createRequest, createRequestType, options)); + var createConfig = createDocument.RootElement.GetProperty("githubMcpToolConfig"); + Assert.True(createConfig.GetProperty("enableAllTools").GetBoolean()); + Assert.Equal("repos", createConfig.GetProperty("additionalToolsets")[0].GetString()); + Assert.True(createConfig.GetProperty("disableFormDeferral").GetBoolean()); + + var resumeRequestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest"); + var resumeRequest = CreateInternalRequest( + resumeRequestType, + ("SessionId", "session-id"), + ("GitHubMcpToolConfig", githubConfig)); + using var resumeDocument = JsonDocument.Parse(JsonSerializer.Serialize(resumeRequest, resumeRequestType, options)); + Assert.True(resumeDocument.RootElement.TryGetProperty("githubMcpToolConfig", out _)); + } + + [Fact] + public void SessionRequests_OmitGitHubMcpToolConfig_WhenUnset() + { + var options = GetSerializerOptions(); + foreach (var requestName in new[] { "CreateSessionRequest", "ResumeSessionRequest" }) + { + var requestType = GetNestedType(typeof(CopilotClient), requestName); + var request = CreateInternalRequest(requestType, ("SessionId", "session-id")); + using var document = JsonDocument.Parse(JsonSerializer.Serialize(request, requestType, options)); + Assert.False(document.RootElement.TryGetProperty("githubMcpToolConfig", out _)); + } + } + [Fact] public void SessionRequests_CanSerializeReasoningSummary_WithSdkOptions() { diff --git a/go/client.go b/go/client.go index 2202e92a1..292a5729e 100644 --- a/go/client.go +++ b/go/client.go @@ -848,6 +848,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses if config.EnableMCPApps { req.RequestMCPApps = Bool(true) } + req.GitHubMCPToolConfig = config.GitHubMCPToolConfig if config.Streaming != nil { req.Streaming = config.Streaming @@ -1225,6 +1226,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, if config.EnableMCPApps { req.RequestMCPApps = Bool(true) } + req.GitHubMCPToolConfig = config.GitHubMCPToolConfig traceparent, tracestate := getTraceContext(ctx) req.Traceparent = traceparent diff --git a/go/client_test.go b/go/client_test.go index 3e4675d0b..2301c990d 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -2363,6 +2363,68 @@ func TestResumeSessionRequest_RequestMCPApps(t *testing.T) { }) } +func TestSessionRequests_GitHubMCPToolConfig(t *testing.T) { + config := &GitHubMCPToolConfig{ + EnableAllTools: Bool(true), + AdditionalToolsets: []string{"repos"}, + AdditionalTools: []string{"get_issue"}, + EnableInsidersMode: Bool(true), + DisableFormDeferral: Bool(true), + } + expected := map[string]any{ + "enableAllTools": true, + "additionalToolsets": []any{"repos"}, + "additionalTools": []any{"get_issue"}, + "enableInsidersMode": true, + "disableFormDeferral": true, + } + + t.Run("create", func(t *testing.T) { + data, err := json.Marshal(createSessionRequest{GitHubMCPToolConfig: config}) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var payload map[string]any + if err := json.Unmarshal(data, &payload); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if !reflect.DeepEqual(payload["githubMcpToolConfig"], expected) { + t.Fatalf("Unexpected githubMcpToolConfig: %#v", payload["githubMcpToolConfig"]) + } + }) + + t.Run("resume", func(t *testing.T) { + data, err := json.Marshal(resumeSessionRequest{ + SessionID: "s1", + GitHubMCPToolConfig: config, + }) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var payload map[string]any + if err := json.Unmarshal(data, &payload); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if !reflect.DeepEqual(payload["githubMcpToolConfig"], expected) { + t.Fatalf("Unexpected githubMcpToolConfig: %#v", payload["githubMcpToolConfig"]) + } + }) + + t.Run("unset is omitted", func(t *testing.T) { + data, err := json.Marshal(createSessionRequest{}) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var payload map[string]any + if err := json.Unmarshal(data, &payload); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if _, ok := payload["githubMcpToolConfig"]; ok { + t.Fatal("Expected githubMcpToolConfig to be omitted") + } + }) +} + func TestResumeSessionRequest_ModeCallbackFlags(t *testing.T) { req := resumeSessionRequest{ SessionID: "s1", diff --git a/go/internal/e2e/session_config_e2e_test.go b/go/internal/e2e/session_config_e2e_test.go index 672a905dc..2ce48e3b3 100644 --- a/go/internal/e2e/session_config_e2e_test.go +++ b/go/internal/e2e/session_config_e2e_test.go @@ -5,10 +5,12 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/http" "os" "path/filepath" "strings" "testing" + "time" copilot "github.com/github/copilot-sdk/go" "github.com/github/copilot-sdk/go/internal/e2e/testharness" @@ -988,6 +990,79 @@ func TestSessionConfigExtrasE2E(t *testing.T) { t.Errorf("Expected toolNames=[view], got %v", toolNames) } }) + + t.Run("should apply GitHub MCP tool config on create", func(t *testing.T) { + ctx.ConfigureForTest(t) + enableAllTools := true + enableInsidersMode := true + disableFormDeferral := true + + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + EnableConfigDiscovery: copilot.Bool(true), + EnableMCPApps: true, + GitHubMCPToolConfig: &copilot.GitHubMCPToolConfig{ + EnableAllTools: &enableAllTools, + AdditionalToolsets: []string{"actions"}, + AdditionalTools: []string{"get_me"}, + EnableInsidersMode: &enableInsidersMode, + DisableFormDeferral: &disableFormDeferral, + }, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + assertGitHubMCPConfigApplied(t, ctx, session) + }) +} + +func assertGitHubMCPConfigApplied(t *testing.T, ctx *testharness.TestContext, session *copilot.Session) { + t.Helper() + if _, err := session.RPC.MCP.List(t.Context()); err != nil { + t.Fatalf("MCP.List failed: %v", err) + } + deadline := time.Now().Add(60 * time.Second) + var lastRequests []testharness.CapturedRequest + for time.Now().Before(deadline) { + requests, err := ctx.GetRequests() + if err == nil { + lastRequests = requests + var writableRequest *testharness.CapturedRequest + hasReadonlyRequest := false + for i := range requests { + request := &requests[i] + if request.URL == "/mcp/readonly" { + hasReadonlyRequest = true + } + if request.Method == http.MethodPost && request.URL == "/mcp" { + writableRequest = request + } + } + if writableRequest != nil { + if hasReadonlyRequest { + t.Fatalf("Expected writable GitHub MCP endpoint, got requests: %+v", requests) + } + assertCapturedHeader(t, writableRequest.Headers, "x-mcp-toolsets", "all") + assertCapturedHeader(t, writableRequest.Headers, "x-mcp-insiders", "true") + return + } + } + time.Sleep(200 * time.Millisecond) + } + t.Fatalf("Timed out waiting for configured GitHub MCP request; captured: %+v", lastRequests) +} + +func assertCapturedHeader(t *testing.T, headers map[string]json.RawMessage, name, expected string) { + t.Helper() + var actual string + if err := json.Unmarshal(headers[name], &actual); err != nil { + t.Fatalf("Failed to decode %s header: %v", name, err) + } + if actual != expected { + t.Fatalf("Expected %s=%q, got %q", name, expected, actual) + } } // createProxyProvider returns a ProviderConfig that points at the test proxy and diff --git a/go/internal/e2e/testharness/context.go b/go/internal/e2e/testharness/context.go index b73153dff..03ebf24cb 100644 --- a/go/internal/e2e/testharness/context.go +++ b/go/internal/e2e/testharness/context.go @@ -312,6 +312,11 @@ func (c *TestContext) GetExchanges() ([]ParsedHttpExchange, error) { return c.proxy.GetExchanges() } +// GetRequests retrieves all captured outbound HTTP requests from the proxy. +func (c *TestContext) GetRequests() ([]CapturedRequest, error) { + return c.proxy.GetRequests() +} + // WaitForExchanges waits until the proxy has captured at least the requested exchanges. func (c *TestContext) WaitForExchanges(t *testing.T, minimumCount int) []ParsedHttpExchange { t.Helper() diff --git a/go/internal/e2e/testharness/proxy.go b/go/internal/e2e/testharness/proxy.go index ec16124bc..2545882bc 100644 --- a/go/internal/e2e/testharness/proxy.go +++ b/go/internal/e2e/testharness/proxy.go @@ -188,6 +188,38 @@ func (p *CapiProxy) GetExchanges() ([]ParsedHttpExchange, error) { return exchanges, nil } +// GetRequests retrieves all captured outbound HTTP requests from the proxy. +func (p *CapiProxy) GetRequests() ([]CapturedRequest, error) { + p.mu.Lock() + url := p.proxyURL + p.mu.Unlock() + + if url == "" { + return nil, fmt.Errorf("proxy not started") + } + + resp, err := http.Get(url + "/requests") + if err != nil { + return nil, fmt.Errorf("failed to get requests: %w", err) + } + defer resp.Body.Close() + + var requests []CapturedRequest + if err := json.NewDecoder(resp.Body).Decode(&requests); err != nil { + return nil, fmt.Errorf("failed to decode requests: %w", err) + } + + return requests, nil +} + +// CapturedRequest represents an outbound HTTP request captured by the proxy. +type CapturedRequest struct { + Method string `json:"method"` + URL string `json:"url"` + Headers map[string]json.RawMessage `json:"headers"` + Body string `json:"body"` +} + // ParsedHttpExchange represents a captured HTTP exchange. type ParsedHttpExchange struct { Request ChatCompletionRequest `json:"request"` diff --git a/go/types.go b/go/types.go index d64aa7786..d625310b1 100644 --- a/go/types.go +++ b/go/types.go @@ -1141,6 +1141,18 @@ func (e ExpConfigEntry) MarshalJSON() ([]byte, error) { return json.Marshal(w) } +// GitHubMCPToolConfig configures the built-in GitHub MCP server. +// +// DisableFormDeferral only applies to the built-in GitHub MCP server and only +// has an effect when MCP Apps and form-backed GitHub tools are enabled. +type GitHubMCPToolConfig struct { + EnableAllTools *bool `json:"enableAllTools,omitempty"` + AdditionalToolsets []string `json:"additionalToolsets,omitempty"` + AdditionalTools []string `json:"additionalTools,omitempty"` + EnableInsidersMode *bool `json:"enableInsidersMode,omitempty"` + DisableFormDeferral *bool `json:"disableFormDeferral,omitempty"` +} + // SessionConfig configures a new session type SessionConfig struct { // SessionID is an optional custom session ID @@ -1376,6 +1388,10 @@ type SessionConfig struct { // cause MCP servers to register UI-enabled tool variants the consumer cannot // display. EnableMCPApps bool + // GitHubMCPToolConfig configures the built-in GitHub MCP server. + // DisableFormDeferral only applies to that server and only has an effect + // when MCP Apps and form-backed GitHub tools are enabled. + GitHubMCPToolConfig *GitHubMCPToolConfig // GitHubToken is an optional per-session GitHub token used for authentication. // When provided, the session authenticates as the token's owner instead of // using the global client-level auth. @@ -1840,6 +1856,10 @@ type ResumeSessionConfig struct { // Experimental: EnableMCPApps is part of an experimental wire-protocol // surface (SEP-1865) and may change or be removed in a future release. EnableMCPApps bool + // GitHubMCPToolConfig configures the built-in GitHub MCP server. + // DisableFormDeferral only applies to that server and only has an effect + // when MCP Apps and form-backed GitHub tools are enabled. + GitHubMCPToolConfig *GitHubMCPToolConfig // Canvases declares canvases this session provides. Sent over the wire on // `session.resume`. See SessionConfig.Canvases. Canvases []CanvasDeclaration @@ -2320,6 +2340,7 @@ type createSessionRequest struct { Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` RequestMCPApps *bool `json:"requestMcpApps,omitempty"` + GitHubMCPToolConfig *GitHubMCPToolConfig `json:"githubMcpToolConfig,omitempty"` GitHubToken string `json:"gitHubToken,omitempty"` RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` Cloud *CloudSessionOptions `json:"cloud,omitempty"` @@ -2412,6 +2433,7 @@ type resumeSessionRequest struct { Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` RequestMCPApps *bool `json:"requestMcpApps,omitempty"` + GitHubMCPToolConfig *GitHubMCPToolConfig `json:"githubMcpToolConfig,omitempty"` GitHubToken string `json:"gitHubToken,omitempty"` RemoteSession rpc.RemoteSessionMode `json:"remoteSession,omitempty"` Canvases []CanvasDeclaration `json:"canvases,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 2b877ae95..1959a9ef8 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -184,6 +184,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess if (config.isEnableMcpApps()) { request.setRequestMcpApps(true); } + request.setGitHubMcpToolConfig(config.getGitHubMcpToolConfig()); if (config.getOnExitPlanMode() != null) { request.setRequestExitPlanMode(true); } @@ -316,6 +317,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo if (config.isEnableMcpApps()) { request.setRequestMcpApps(true); } + request.setGitHubMcpToolConfig(config.getGitHubMcpToolConfig()); if (config.getOnExitPlanMode() != null) { request.setRequestExitPlanMode(true); } diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 80926aae2..1755fddbf 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -196,6 +196,9 @@ public final class CreateSessionRequest { @JsonProperty("requestMcpApps") private Boolean requestMcpApps; + @JsonProperty("githubMcpToolConfig") + private GitHubMcpToolConfig githubMcpToolConfig; + @JsonProperty("requestExitPlanMode") private Boolean requestExitPlanMode; @@ -918,6 +921,16 @@ public void clearRequestMcpApps() { this.requestMcpApps = null; } + /** Gets the GitHub MCP tool configuration. @return the configuration */ + public GitHubMcpToolConfig getGitHubMcpToolConfig() { + return githubMcpToolConfig; + } + + /** Sets the GitHub MCP tool configuration. @param config the value */ + public void setGitHubMcpToolConfig(GitHubMcpToolConfig config) { + this.githubMcpToolConfig = config; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; diff --git a/java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java b/java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java new file mode 100644 index 000000000..75a8e3016 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java @@ -0,0 +1,82 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Configuration for the built-in GitHub MCP server. + * + *

+ * {@code disableFormDeferral} only applies to the built-in GitHub MCP server + * and only has an effect when MCP Apps and form-backed GitHub tools are + * enabled. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class GitHubMcpToolConfig { + + @JsonProperty("enableAllTools") + private Boolean enableAllTools; + + @JsonProperty("additionalToolsets") + private List additionalToolsets; + + @JsonProperty("additionalTools") + private List additionalTools; + + @JsonProperty("enableInsidersMode") + private Boolean enableInsidersMode; + + @JsonProperty("disableFormDeferral") + private Boolean disableFormDeferral; + + public Boolean getEnableAllTools() { + return enableAllTools; + } + + public GitHubMcpToolConfig setEnableAllTools(Boolean enableAllTools) { + this.enableAllTools = enableAllTools; + return this; + } + + public List getAdditionalToolsets() { + return additionalToolsets; + } + + public GitHubMcpToolConfig setAdditionalToolsets(List additionalToolsets) { + this.additionalToolsets = additionalToolsets; + return this; + } + + public List getAdditionalTools() { + return additionalTools; + } + + public GitHubMcpToolConfig setAdditionalTools(List additionalTools) { + this.additionalTools = additionalTools; + return this; + } + + public Boolean getEnableInsidersMode() { + return enableInsidersMode; + } + + public GitHubMcpToolConfig setEnableInsidersMode(Boolean enableInsidersMode) { + this.enableInsidersMode = enableInsidersMode; + return this; + } + + public Boolean getDisableFormDeferral() { + return disableFormDeferral; + } + + public GitHubMcpToolConfig setDisableFormDeferral(Boolean disableFormDeferral) { + this.disableFormDeferral = disableFormDeferral; + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index a42941faf..42419e5a3 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -99,6 +99,7 @@ public class ResumeSessionConfig { private ExitPlanModeHandler onExitPlanMode; private AutoModeSwitchHandler onAutoModeSwitch; private boolean enableMcpApps; + private GitHubMcpToolConfig githubMcpToolConfig; private String gitHubToken; private String remoteSession; private CopilotExpAssignmentResponse expAssignments; @@ -1630,6 +1631,27 @@ public ResumeSessionConfig setEnableMcpApps(boolean enableMcpApps) { return this; } + /** + * Gets the configuration for the built-in GitHub MCP server. + * + * @return the GitHub MCP configuration, or {@code null} + */ + public GitHubMcpToolConfig getGitHubMcpToolConfig() { + return githubMcpToolConfig; + } + + /** + * Sets the configuration for the built-in GitHub MCP server. + * + * @param githubMcpToolConfig + * the GitHub MCP configuration + * @return this config instance for method chaining + */ + public ResumeSessionConfig setGitHubMcpToolConfig(GitHubMcpToolConfig githubMcpToolConfig) { + this.githubMcpToolConfig = githubMcpToolConfig; + return this; + } + /** * Gets the exit-plan-mode request handler. * @@ -1866,6 +1888,7 @@ public ResumeSessionConfig clone() { copy.onExitPlanMode = this.onExitPlanMode; copy.onAutoModeSwitch = this.onAutoModeSwitch; copy.enableMcpApps = this.enableMcpApps; + copy.githubMcpToolConfig = this.githubMcpToolConfig; copy.gitHubToken = this.gitHubToken; copy.remoteSession = this.remoteSession; copy.expAssignments = this.expAssignments; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 014af9cf1..55bd08b91 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -201,6 +201,9 @@ public final class ResumeSessionRequest { @JsonProperty("requestMcpApps") private Boolean requestMcpApps; + @JsonProperty("githubMcpToolConfig") + private GitHubMcpToolConfig githubMcpToolConfig; + @JsonProperty("requestExitPlanMode") private Boolean requestExitPlanMode; @@ -943,6 +946,16 @@ public void clearRequestMcpApps() { this.requestMcpApps = null; } + /** Gets the GitHub MCP tool configuration. @return the configuration */ + public GitHubMcpToolConfig getGitHubMcpToolConfig() { + return githubMcpToolConfig; + } + + /** Sets the GitHub MCP tool configuration. @param config the value */ + public void setGitHubMcpToolConfig(GitHubMcpToolConfig config) { + this.githubMcpToolConfig = config; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 7dd3fc50f..7033ba557 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -99,6 +99,7 @@ public class SessionConfig { private ExitPlanModeHandler onExitPlanMode; private AutoModeSwitchHandler onAutoModeSwitch; private boolean enableMcpApps; + private GitHubMcpToolConfig githubMcpToolConfig; private String gitHubToken; private String remoteSession; private CloudSessionOptions cloud; @@ -1712,6 +1713,27 @@ public SessionConfig setEnableMcpApps(boolean enableMcpApps) { return this; } + /** + * Gets the configuration for the built-in GitHub MCP server. + * + * @return the GitHub MCP configuration, or {@code null} + */ + public GitHubMcpToolConfig getGitHubMcpToolConfig() { + return githubMcpToolConfig; + } + + /** + * Sets the configuration for the built-in GitHub MCP server. + * + * @param githubMcpToolConfig + * the GitHub MCP configuration + * @return this config instance for method chaining + */ + public SessionConfig setGitHubMcpToolConfig(GitHubMcpToolConfig githubMcpToolConfig) { + this.githubMcpToolConfig = githubMcpToolConfig; + return this; + } + /** * Gets the exit-plan-mode request handler. * @@ -2001,6 +2023,7 @@ public SessionConfig clone() { copy.onExitPlanMode = this.onExitPlanMode; copy.onAutoModeSwitch = this.onAutoModeSwitch; copy.enableMcpApps = this.enableMcpApps; + copy.githubMcpToolConfig = this.githubMcpToolConfig; copy.gitHubToken = this.gitHubToken; copy.remoteSession = this.remoteSession; copy.cloud = this.cloud; diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 78dede074..505f93a5a 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -25,6 +25,7 @@ import com.github.copilot.rpc.ElicitationResultAction; import com.github.copilot.rpc.ExitPlanModeResult; import com.github.copilot.rpc.ExpConfigEntry; +import com.github.copilot.rpc.GitHubMcpToolConfig; import com.github.copilot.rpc.LargeToolOutputConfig; import com.github.copilot.rpc.MemoryConfiguration; import com.github.copilot.rpc.ResumeSessionConfig; @@ -989,4 +990,23 @@ void testClonePreservesAndForwardsExpAssignments() throws Exception { assertEquals(resumeAssignments, resumeRequest.getExpAssignments()); assertTrue(mapper.writeValueAsString(resumeRequest).contains("\"Id\":\"exp-resume\"")); } + + @Test + void githubMcpToolConfigIsMappedAndSerializedForCreateAndResume() throws Exception { + var config = new GitHubMcpToolConfig().setEnableAllTools(true).setAdditionalToolsets(List.of("repos")) + .setAdditionalTools(List.of("get_issue")).setEnableInsidersMode(true).setDisableFormDeferral(true); + var createRequest = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setGitHubMcpToolConfig(config), + "session-1"); + var resumeRequest = SessionRequestBuilder.buildResumeRequest("session-1", + new ResumeSessionConfig().setGitHubMcpToolConfig(config)); + + assertSame(config, createRequest.getGitHubMcpToolConfig()); + assertSame(config, resumeRequest.getGitHubMcpToolConfig()); + var mapper = JsonRpcClient.getObjectMapper(); + assertTrue(mapper.writeValueAsString(createRequest).contains("\"githubMcpToolConfig\"")); + assertTrue(mapper.writeValueAsString(resumeRequest).contains("\"githubMcpToolConfig\"")); + assertFalse( + mapper.writeValueAsString(SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "session-2")) + .contains("\"githubMcpToolConfig\"")); + } } diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 53821c403..78290bf68 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1559,6 +1559,9 @@ export class CopilotClient { requestUserInput: !!config.onUserInputRequest, requestElicitation: !!config.onElicitationRequest, ...(config.enableMcpApps ? { requestMcpApps: true } : {}), + ...(config.githubMcpToolConfig != null + ? { githubMcpToolConfig: config.githubMcpToolConfig } + : {}), requestExitPlanMode: !!config.onExitPlanModeRequest, requestAutoModeSwitch: !!config.onAutoModeSwitchRequest, hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)), @@ -1797,6 +1800,9 @@ export class CopilotClient { requestUserInput: !!config.onUserInputRequest, requestElicitation: !!config.onElicitationRequest, ...(config.enableMcpApps ? { requestMcpApps: true } : {}), + ...(config.githubMcpToolConfig != null + ? { githubMcpToolConfig: config.githubMcpToolConfig } + : {}), requestExitPlanMode: !!config.onExitPlanModeRequest, requestAutoModeSwitch: !!config.onAutoModeSwitchRequest, hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)), diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 43d90b2f3..8d79a71a1 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -88,6 +88,7 @@ export type { ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, + GitHubMcpToolConfig, GitHubTelemetryNotification, GitHubTelemetryEvent, GitHubTelemetryClientInfo, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index ea70ae30f..acba82667 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2030,6 +2030,20 @@ export interface CopilotExpAssignmentResponse { AssignmentContext: string; } +/** + * Configuration for the built-in GitHub MCP server. + * + * `disableFormDeferral` only applies to the built-in GitHub MCP server and + * only has an effect when MCP Apps and form-backed GitHub tools are enabled. + */ +export interface GitHubMcpToolConfig { + enableAllTools?: boolean; + additionalToolsets?: string[]; + additionalTools?: string[]; + enableInsidersMode?: boolean; + disableFormDeferral?: boolean; +} + /** * Shared configuration fields used by both {@link SessionConfig} (for * creating a new session) and {@link ResumeSessionConfig} (for resuming @@ -2359,6 +2373,14 @@ export interface SessionConfigBase { */ enableMcpApps?: boolean; + /** + * Configuration for the built-in GitHub MCP server. + * + * `disableFormDeferral` only applies to the built-in GitHub MCP server and + * only has an effect when MCP Apps and form-backed GitHub tools are enabled. + */ + githubMcpToolConfig?: GitHubMcpToolConfig; + /** * Handler for exit-plan-mode requests from the agent. * When provided, enables `exitPlanMode.request` callbacks. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index e4b23deb9..124261527 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -136,6 +136,55 @@ describe("CopilotClient", () => { }); }); + it("forwards GitHub MCP tool config on create and resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + const githubMcpToolConfig = { + enableAllTools: true, + additionalToolsets: ["repos"], + additionalTools: ["get_issue"], + enableInsidersMode: true, + disableFormDeferral: true, + }; + + const session = await client.createSession({ githubMcpToolConfig }); + await client.resumeSession(session.sessionId, { githubMcpToolConfig }); + + expect(spy.mock.calls.find(([method]) => method === "session.create")![1]).toMatchObject({ + githubMcpToolConfig, + }); + expect(spy.mock.calls.find(([method]) => method === "session.resume")![1]).toMatchObject({ + githubMcpToolConfig, + }); + }); + + it("omits GitHub MCP tool config when unset", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + await client.createSession({}); + + expect( + spy.mock.calls.find(([method]) => method === "session.create")![1] + ).not.toHaveProperty("githubMcpToolConfig"); + }); + it("passes MCP OAuth requests through when optional metadata is absent", async () => { let observedRequest: any; const session = new CopilotSession( diff --git a/nodejs/test/e2e/harness/CapiProxy.ts b/nodejs/test/e2e/harness/CapiProxy.ts index a6232587e..c25d422f9 100644 --- a/nodejs/test/e2e/harness/CapiProxy.ts +++ b/nodejs/test/e2e/harness/CapiProxy.ts @@ -2,6 +2,7 @@ import { spawn } from "child_process"; import { resolve } from "path"; import { createInterface } from "readline"; import { expect } from "vitest"; +import type { CapturedRequest } from "../../../../test/harness/replayingCapiProxy"; import { CopilotUserResponse, ParsedHttpExchange, @@ -121,6 +122,11 @@ export class CapiProxy { return await response.json(); } + async getRequests(): Promise { + const response = await fetch(`${this.proxyUrl}/requests`, { method: "GET" }); + return await response.json(); + } + async stop(skipWritingCache?: boolean): Promise { const url = skipWritingCache ? `${this.proxyUrl}/stop?skipWritingCache=true` diff --git a/nodejs/test/e2e/session_config.e2e.test.ts b/nodejs/test/e2e/session_config.e2e.test.ts index 98b1a0bfa..85137e0ff 100644 --- a/nodejs/test/e2e/session_config.e2e.test.ts +++ b/nodejs/test/e2e/session_config.e2e.test.ts @@ -222,6 +222,23 @@ describe("Session Configuration", async () => { return (exchange.request.tools ?? []).map((t) => t.function.name); } + async function expectGitHubMcpConfigApplied(session: CopilotSession): Promise { + await session.rpc.mcp.list(); + await retry("capture configured GitHub MCP request", async () => { + const requests = await openAiEndpoint.getRequests(); + const request = requests.find( + (entry) => entry.method === "POST" && entry.url === "/mcp" + ); + expect( + request, + `captured requests: ${requests.map((entry) => `${entry.method} ${entry.url}`).join(", ")}` + ).toBeDefined(); + expect(request?.headers["x-mcp-toolsets"]).toBe("all"); + expect(request?.headers["x-mcp-insiders"]).toBe("true"); + expect(requests.some((entry) => entry.url === "/mcp/readonly")).toBe(false); + }); + } + async function sendAndGetNextExchange( session: { sendAndWait(options: { prompt: string }): Promise }, prompt: string @@ -848,4 +865,25 @@ describe("Session Configuration", async () => { await session2.disconnect(); } }); + + it("should apply GitHub MCP tool config on create", async () => { + const session = await client.createSession({ + onPermissionRequest: approveAll, + enableConfigDiscovery: true, + enableMcpApps: true, + githubMcpToolConfig: { + enableAllTools: true, + additionalToolsets: ["actions"], + additionalTools: ["get_me"], + enableInsidersMode: true, + disableFormDeferral: true, + }, + }); + + try { + await expectGitHubMcpConfigApplied(session); + } finally { + await session.disconnect(); + } + }); }); diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 8b62c3c25..8b0100df2 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -113,6 +113,7 @@ ExitPlanModeHandler, ExitPlanModeRequest, ExitPlanModeResult, + GitHubMcpToolConfig, InfiniteSessionConfig, InputOptions, LargeToolOutputConfig, @@ -248,6 +249,7 @@ "GetAuthStatusResponse", "BearerTokenProvider", "GetStatusResponse", + "GitHubMcpToolConfig", "GitHubTelemetryClientInfo", "GitHubTelemetryEvent", "GitHubTelemetryNotification", diff --git a/python/copilot/client.py b/python/copilot/client.py index d0aeba8b7..f7f0a4eb2 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -95,6 +95,7 @@ DefaultAgentConfig, ElicitationHandler, ExitPlanModeHandler, + GitHubMcpToolConfig, InfiniteSessionConfig, LargeToolOutputConfig, McpAuthHandler, @@ -335,6 +336,22 @@ def _tool_search_to_wire(config: Mapping[str, Any]) -> dict[str, Any]: return wire +def _github_mcp_tool_config_to_wire(config: Mapping[str, Any]) -> dict[str, Any]: + """Convert a ``GitHubMcpToolConfig`` mapping to wire format.""" + wire: dict[str, Any] = {} + if "enable_all_tools" in config: + wire["enableAllTools"] = config["enable_all_tools"] + if "additional_toolsets" in config: + wire["additionalToolsets"] = config["additional_toolsets"] + if "additional_tools" in config: + wire["additionalTools"] = config["additional_tools"] + if "enable_insiders_mode" in config: + wire["enableInsidersMode"] = config["enable_insiders_mode"] + if "disable_form_deferral" in config: + wire["disableFormDeferral"] = config["disable_form_deferral"] + return wire + + class TelemetryConfig(TypedDict, total=False): """Configuration for OpenTelemetry integration with the Copilot CLI.""" @@ -2068,6 +2085,7 @@ async def create_session( canvas_handler: CanvasHandler | None = None, exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, + github_mcp_tool_config: GitHubMcpToolConfig | None = None, ) -> CopilotSession: """ Create a new conversation session with the Copilot CLI. @@ -2184,6 +2202,16 @@ async def create_session( override) is on; otherwise the request is silently dropped. Inspect ``capabilities.ui.mcpApps`` on the create response to detect the drop. + github_mcp_tool_config: Configuration for the built-in GitHub MCP + server, sent as ``githubMcpToolConfig`` on ``session.create``. + Supports ``enable_all_tools``, ``additional_toolsets``, + ``additional_tools``, ``enable_insiders_mode``, and + ``disable_form_deferral``. Setting ``disable_form_deferral`` + makes form-backed GitHub write tools execute directly instead + of returning an awaiting-form stub; it does not enable MCP Apps + on its own and has no effect unless MCP Apps are enabled for + the session (see ``enable_mcp_apps``). Omitted from the wire + payload entirely when None. exp_assignments: ExP assignment ("flight") data injected by a trusted integrator, in the same JSON shape the Copilot CLI fetches from the experimentation service @@ -2310,6 +2338,8 @@ async def create_session( payload["requestElicitation"] = bool(on_elicitation_request) if enable_mcp_apps: payload["requestMcpApps"] = True + if github_mcp_tool_config is not None: + payload["githubMcpToolConfig"] = _github_mcp_tool_config_to_wire(github_mcp_tool_config) payload["requestExitPlanMode"] = bool(on_exit_plan_mode_request) payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch_request) @@ -2745,6 +2775,7 @@ async def resume_session( open_canvases: list[OpenCanvasInstance] | None = None, exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, + github_mcp_tool_config: GitHubMcpToolConfig | None = None, ) -> CopilotSession: """ Resume an existing conversation session by its ID. @@ -2858,6 +2889,16 @@ async def resume_session( override) is on; otherwise the request is silently dropped. Inspect ``capabilities.ui.mcpApps`` on the resume response to detect the drop. + github_mcp_tool_config: Configuration for the built-in GitHub MCP + server, sent as ``githubMcpToolConfig`` on ``session.resume``. + Supports ``enable_all_tools``, ``additional_toolsets``, + ``additional_tools``, ``enable_insiders_mode``, and + ``disable_form_deferral``. Setting ``disable_form_deferral`` + makes form-backed GitHub write tools execute directly instead + of returning an awaiting-form stub; it does not enable MCP Apps + on its own and has no effect unless MCP Apps are enabled for + the session (see ``enable_mcp_apps``). Omitted from the wire + payload entirely when None. continue_pending_work: When True, instructs the runtime to continue any tool calls or permission prompts that were still pending when the session was last suspended. When False (the default), the runtime @@ -3016,6 +3057,8 @@ async def resume_session( payload["requestElicitation"] = bool(on_elicitation_request) if enable_mcp_apps: payload["requestMcpApps"] = True + if github_mcp_tool_config is not None: + payload["githubMcpToolConfig"] = _github_mcp_tool_config_to_wire(github_mcp_tool_config) payload["requestExitPlanMode"] = bool(on_exit_plan_mode_request) payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch_request) diff --git a/python/copilot/session.py b/python/copilot/session.py index 72b055e67..4474ce562 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -1098,6 +1098,22 @@ class MCPHTTPServerConfig(TypedDict, total=False): MCPServerConfig = MCPStdioServerConfig | MCPHTTPServerConfig + +class GitHubMcpToolConfig(TypedDict, total=False): + """Configuration for the built-in GitHub MCP server. + + ``disable_form_deferral`` only applies to the built-in GitHub MCP server + and only has an effect when MCP Apps and form-backed GitHub tools are + enabled. + """ + + enable_all_tools: bool + additional_toolsets: list[str] + additional_tools: list[str] + enable_insiders_mode: bool + disable_form_deferral: bool + + # ============================================================================ # Custom Agent Configuration Types # ============================================================================ diff --git a/python/test_client.py b/python/test_client.py index a37d8dce2..ba353bde3 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -504,6 +504,46 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_github_mcp_tool_config(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + config = { + "enable_all_tools": True, + "additional_toolsets": ["repos"], + "additional_tools": ["get_issue"], + "enable_insiders_mode": True, + "disable_form_deferral": True, + } + session = await client.create_session(github_mcp_tool_config=config) + await client.resume_session(session.session_id, github_mcp_tool_config=config) + + expected = { + "enableAllTools": True, + "additionalToolsets": ["repos"], + "additionalTools": ["get_issue"], + "enableInsidersMode": True, + "disableFormDeferral": True, + } + assert captured["session.create"]["githubMcpToolConfig"] == expected + assert captured["session.resume"]["githubMcpToolConfig"] == expected + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_forward_reasoning_summary(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) diff --git a/rust/src/types.rs b/rust/src/types.rs index b93633f5d..b5c180e4a 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -825,6 +825,80 @@ impl ToolSearchConfig { } } +/// Configuration for the built-in GitHub MCP server. +/// +/// `disable_form_deferral` only applies to the built-in GitHub MCP server and +/// only has an effect when MCP Apps and form-backed GitHub tools are enabled. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct GitHubMcpToolConfig { + /// Whether all GitHub MCP tools are enabled. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enable_all_tools: Option, + /// Additional GitHub MCP toolsets to enable. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub additional_toolsets: Option>, + /// Additional GitHub MCP tools to enable. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub additional_tools: Option>, + /// Whether GitHub MCP insiders mode is enabled. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enable_insiders_mode: Option, + /// Disables form deferral for GitHub MCP tools. This only applies to the + /// built-in GitHub MCP server and only has an effect when MCP Apps and + /// form-backed GitHub tools are enabled. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_form_deferral: Option, +} + +impl GitHubMcpToolConfig { + /// Construct an empty GitHub MCP tool configuration. + pub fn new() -> Self { + Self::default() + } + + /// Set whether all GitHub MCP tools are enabled. + pub fn with_enable_all_tools(mut self, value: bool) -> Self { + self.enable_all_tools = Some(value); + self + } + + /// Set the additional GitHub MCP toolsets to enable. + pub fn with_additional_toolsets(mut self, values: I) -> Self + where + I: IntoIterator, + S: Into, + { + self.additional_toolsets = Some(values.into_iter().map(Into::into).collect()); + self + } + + /// Set the additional GitHub MCP tools to enable. + pub fn with_additional_tools(mut self, values: I) -> Self + where + I: IntoIterator, + S: Into, + { + self.additional_tools = Some(values.into_iter().map(Into::into).collect()); + self + } + + /// Set whether GitHub MCP insiders mode is enabled. + pub fn with_enable_insiders_mode(mut self, value: bool) -> Self { + self.enable_insiders_mode = Some(value); + self + } + + /// Disable form deferral for GitHub MCP tools. This only applies to the + /// built-in GitHub MCP server and only has an effect when MCP Apps and + /// form-backed GitHub tools are enabled. + pub fn with_disable_form_deferral(mut self, value: bool) -> Self { + self.disable_form_deferral = Some(value); + self + } +} + /// Configures infinite sessions: persistent workspaces with automatic /// context-window compaction. /// @@ -1833,6 +1907,11 @@ pub struct SessionConfig { /// /// Defaults to `None` (treated as `false`). pub enable_mcp_apps: Option, + /// Configuration for the built-in GitHub MCP server. + /// + /// `disable_form_deferral` only applies to that server and only has an + /// effect when MCP Apps and form-backed GitHub tools are enabled. + pub github_mcp_tool_config: Option, /// Skill directory paths passed through to the GitHub Copilot CLI. pub skill_directories: Option>, /// Additional directories to search for custom instruction files. @@ -2166,6 +2245,7 @@ impl Default for SessionConfig { enable_skills: None, embedding_cache_storage: None, enable_mcp_apps: None, + github_mcp_tool_config: None, skill_directories: None, instruction_directories: None, plugin_directories: None, @@ -2324,6 +2404,7 @@ impl SessionConfig { request_auto_mode_switch, request_elicitation, request_mcp_apps: self.enable_mcp_apps.unwrap_or(false), + github_mcp_tool_config: self.github_mcp_tool_config, hooks: hooks_flag, skill_directories: self.skill_directories, instruction_directories: self.instruction_directories, @@ -2705,6 +2786,12 @@ impl SessionConfig { self } + /// Set the built-in GitHub MCP server configuration. + pub fn with_github_mcp_tool_config(mut self, config: GitHubMcpToolConfig) -> Self { + self.github_mcp_tool_config = Some(config); + self + } + /// Set skill directory paths passed through to the CLI. pub fn with_skill_directories(mut self, paths: I) -> Self where @@ -3039,6 +3126,11 @@ pub struct ResumeSessionConfig { /// Enable MCP Apps (SEP-1865) UI passthrough on resume. See /// [`SessionConfig::enable_mcp_apps`]. Defaults to `None` (treated as `false`). pub enable_mcp_apps: Option, + /// Configuration for the built-in GitHub MCP server. + /// + /// `disable_form_deferral` only applies to that server and only has an + /// effect when MCP Apps and form-backed GitHub tools are enabled. + pub github_mcp_tool_config: Option, /// Skill directory paths passed through to the GitHub Copilot CLI on resume. pub skill_directories: Option>, /// Additional directories to search for custom instruction files on @@ -3381,6 +3473,7 @@ impl ResumeSessionConfig { request_auto_mode_switch, request_elicitation, request_mcp_apps: self.enable_mcp_apps.unwrap_or(false), + github_mcp_tool_config: self.github_mcp_tool_config, hooks: hooks_flag, skill_directories: self.skill_directories, instruction_directories: self.instruction_directories, @@ -3473,6 +3566,7 @@ impl ResumeSessionConfig { enable_skills: None, embedding_cache_storage: None, enable_mcp_apps: None, + github_mcp_tool_config: None, skill_directories: None, instruction_directories: None, plugin_directories: None, @@ -3832,6 +3926,12 @@ impl ResumeSessionConfig { self } + /// Set the built-in GitHub MCP server configuration. + pub fn with_github_mcp_tool_config(mut self, config: GitHubMcpToolConfig) -> Self { + self.github_mcp_tool_config = Some(config); + self + } + /// Set skill directory paths passed through to the CLI on resume. pub fn with_skill_directories(mut self, paths: I) -> Self where @@ -5504,12 +5604,12 @@ mod tests { AgentMode, Attachment, AttachmentLineRange, AttachmentSelectionPosition, AttachmentSelectionRange, AzureProviderOptions, CapiSessionOptions, ConnectionState, CopilotExpAssignmentResponse, CustomAgentConfig, DeliveryMode, ExpConfigEntry, - ExpFlagValue, ExtensionInfo, GitHubReferenceType, InfiniteSessionConfig, - LargeToolOutputConfig, McpServerConfig, McpStdioServerConfig, MemoryConfiguration, - NamedProviderConfig, ProviderConfig, ProviderModelConfig, ReasoningSummary, - ResumeSessionConfig, SessionConfig, SessionEvent, SessionId, SystemMessageConfig, Tool, - ToolBinaryResult, ToolResult, ToolResultExpanded, ToolResultResponse, - ensure_attachment_display_names, + ExpFlagValue, ExtensionInfo, GitHubMcpToolConfig, GitHubReferenceType, + InfiniteSessionConfig, LargeToolOutputConfig, McpServerConfig, McpStdioServerConfig, + MemoryConfiguration, NamedProviderConfig, ProviderConfig, ProviderModelConfig, + ReasoningSummary, ResumeSessionConfig, SessionConfig, SessionEvent, SessionId, + SystemMessageConfig, Tool, ToolBinaryResult, ToolResult, ToolResultExpanded, + ToolResultResponse, ensure_attachment_display_names, }; use crate::generated::session_events::TypedSessionEvent; @@ -5843,6 +5943,47 @@ mod tests { assert_eq!(json["requestMcpApps"], serde_json::Value::Bool(true)); } + #[test] + fn github_mcp_tool_config_serializes_for_create_and_resume() { + let github_config = GitHubMcpToolConfig::new() + .with_enable_all_tools(true) + .with_additional_toolsets(["repos"]) + .with_additional_tools(["get_issue"]) + .with_enable_insiders_mode(true) + .with_disable_form_deferral(true); + + let (create_wire, _) = SessionConfig::default() + .with_github_mcp_tool_config(github_config.clone()) + .into_wire(Some(SessionId::from("github-mcp"))) + .expect("create config has no duplicate handlers"); + assert_eq!( + serde_json::to_value(&create_wire).unwrap()["githubMcpToolConfig"], + serde_json::json!({ + "enableAllTools": true, + "additionalToolsets": ["repos"], + "additionalTools": ["get_issue"], + "enableInsidersMode": true, + "disableFormDeferral": true, + }) + ); + + let (resume_wire, _) = ResumeSessionConfig::new(SessionId::from("github-mcp")) + .with_github_mcp_tool_config(github_config) + .into_wire() + .expect("resume config has no duplicate handlers"); + assert!(resume_wire.github_mcp_tool_config.is_some()); + + let (unset_wire, _) = SessionConfig::default() + .into_wire(Some(SessionId::from("github-mcp-unset"))) + .expect("default config has no duplicate handlers"); + assert!( + serde_json::to_value(&unset_wire) + .unwrap() + .get("githubMcpToolConfig") + .is_none() + ); + } + #[test] fn memory_configuration_constructors_and_serde() { assert!(MemoryConfiguration::enabled().enabled); diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 90b84be28..4dc756909 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -25,9 +25,10 @@ use crate::generated::api_types::{ use crate::generated::session_events::ReasoningSummary; use crate::types::{ CanvasProviderIdentity, CapiSessionOptions, CloudSessionOptions, CustomAgentConfig, - DefaultAgentConfig, ExtensionInfo, InfiniteSessionConfig, LargeToolOutputConfig, - McpServerConfig, MemoryConfiguration, NamedProviderConfig, ProviderConfig, ProviderModelConfig, - SessionId, SessionLimitsConfig, SystemMessageConfig, Tool, ToolSearchConfig, + DefaultAgentConfig, ExtensionInfo, GitHubMcpToolConfig, InfiniteSessionConfig, + LargeToolOutputConfig, McpServerConfig, MemoryConfiguration, NamedProviderConfig, + ProviderConfig, ProviderModelConfig, SessionId, SessionLimitsConfig, SystemMessageConfig, Tool, + ToolSearchConfig, }; /// Wire representation of a slash command (name + description only). The @@ -113,6 +114,8 @@ pub(crate) struct SessionCreateWire { pub request_auto_mode_switch: bool, pub request_elicitation: bool, pub request_mcp_apps: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub github_mcp_tool_config: Option, pub hooks: bool, #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, @@ -251,6 +254,8 @@ pub(crate) struct SessionResumeWire { pub request_auto_mode_switch: bool, pub request_elicitation: bool, pub request_mcp_apps: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub github_mcp_tool_config: Option, pub hooks: bool, #[serde(skip_serializing_if = "Option::is_none")] pub skill_directories: Option>, diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 9b83cfe79..47ebda9f7 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -19,6 +19,7 @@ import { CapturingHttpProxy, PerformRequestOptions, } from "./capturingHttpProxy"; +export type { CapturedRequest } from "./capturingHttpProxy"; import { anthropicMessagesEndpoint, anthropicMessagesRequestToChatCompletion, @@ -327,6 +328,20 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { return; } + // Handle /requests endpoint for retrieving all captured outbound requests. + if ( + options.requestOptions.path === "/requests" && + options.requestOptions.method === "GET" + ) { + const requests = this.exchanges + .map((exchange) => exchange.request) + .filter((request) => request.url !== "/requests"); + options.onResponseStart(200, { "content-type": "application/json" }); + options.onData(Buffer.from(JSON.stringify(requests))); + options.onResponseEnd(); + return; + } + // Handle /copilot_internal/user endpoint for per-session auth. // This must run before the state guard below: the CLI authenticates and // calls /copilot_internal/user at startup, which can race ahead of the diff --git a/test/snapshots/session_config/should_apply_github_mcp_tool_config_on_create.yaml b/test/snapshots/session_config/should_apply_github_mcp_tool_config_on_create.yaml new file mode 100644 index 000000000..056351ddb --- /dev/null +++ b/test/snapshots/session_config/should_apply_github_mcp_tool_config_on_create.yaml @@ -0,0 +1,3 @@ +models: + - claude-sonnet-4.5 +conversations: [] From e6b06f54d859495a7f399e67f1780990382e161b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 31 Jul 2026 23:45:34 -0400 Subject: [PATCH 04/72] Deflake background task removal E2E tests (#2190) * Fix background task removal E2E race Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ebc2e7-3ffd-4573-becd-bb1954344115 --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b3ebc2e7-3ffd-4573-becd-bb1954344115 --- .../test/E2E/RpcTasksAndHandlersE2ETests.cs | 39 ++++++++++++------- python/e2e/test_rpc_tasks_and_handlers_e2e.py | 32 +++++++++------ 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/dotnet/test/E2E/RpcTasksAndHandlersE2ETests.cs b/dotnet/test/E2E/RpcTasksAndHandlersE2ETests.cs index 989fef7c5..640e4f72f 100644 --- a/dotnet/test/E2E/RpcTasksAndHandlersE2ETests.cs +++ b/dotnet/test/E2E/RpcTasksAndHandlersE2ETests.cs @@ -152,29 +152,38 @@ await TestHelper.WaitForConditionAsync( async () => { task = await FindAgentTaskAsync(session, started.AgentId); - return task?.LatestResponse?.Contains("TASK_AGENT_DONE", StringComparison.Ordinal) == true - || task?.Result?.Contains("TASK_AGENT_DONE", StringComparison.Ordinal) == true - || task?.Status == GitHub.Copilot.Rpc.TaskStatus.Completed - || task?.Status == GitHub.Copilot.Rpc.TaskStatus.Failed; + return task is null + || task.Status == GitHub.Copilot.Rpc.TaskStatus.Completed + || task.Status == GitHub.Copilot.Rpc.TaskStatus.Failed + || task.Status == GitHub.Copilot.Rpc.TaskStatus.Cancelled + || task.Status == GitHub.Copilot.Rpc.TaskStatus.Idle; }, timeout: TimeSpan.FromSeconds(60), timeoutMessage: $"Background agent task '{started.AgentId}' did not produce a final observable state."); - Assert.NotNull(task); - Assert.Contains("TASK_AGENT_DONE", task.LatestResponse ?? task.Result ?? string.Empty); - await taskCompletionNotification.Task.WaitAsync(TimeSpan.FromSeconds(30)); - - if (task.Status == GitHub.Copilot.Rpc.TaskStatus.Idle) + if (task is not null) { - var cancel = await session.Rpc.Tasks.CancelAsync(started.AgentId); - Assert.True(cancel.Cancelled); - } + Assert.Contains("TASK_AGENT_DONE", task.LatestResponse ?? task.Result ?? string.Empty); + + if (task.Status == GitHub.Copilot.Rpc.TaskStatus.Idle) + { + var cancel = await session.Rpc.Tasks.CancelAsync(started.AgentId); + Assert.True(cancel.Cancelled); + } - var remove = await session.Rpc.Tasks.RemoveAsync(started.AgentId); - Assert.True(remove.Removed); + var remove = await session.Rpc.Tasks.RemoveAsync(started.AgentId); + // Completion delivery also removes finished tasks, so this call may lose that race. + Assert.True( + remove.Removed || taskCompletionNotification.Task.IsCompleted, + $"Background agent task '{started.AgentId}' was not removed before its completion notification was delivered."); + } var afterRemove = await session.Rpc.Tasks.ListAsync(); - Assert.DoesNotContain(afterRemove.Tasks.OfType(), t => string.Equals(t.Id, started.AgentId, StringComparison.Ordinal)); + var taskAfterRemove = afterRemove.Tasks.OfType() + .SingleOrDefault(t => string.Equals(t.Id, started.AgentId, StringComparison.Ordinal)); + Assert.Null(taskAfterRemove); + + await taskCompletionNotification.Task.WaitAsync(TimeSpan.FromSeconds(30)); } [Fact] diff --git a/python/e2e/test_rpc_tasks_and_handlers_e2e.py b/python/e2e/test_rpc_tasks_and_handlers_e2e.py index 6a99cbb75..f0dd8f757 100644 --- a/python/e2e/test_rpc_tasks_and_handlers_e2e.py +++ b/python/e2e/test_rpc_tasks_and_handlers_e2e.py @@ -445,20 +445,28 @@ def on_event(event): 60.0, f"Task {task_id} did not produce a final observable state", ) - assert found_task is not None, f"Task {task_id} disappeared before it completed" - assert "TASK_AGENT_DONE" in (found_task.latest_response or found_task.result or "") - await asyncio.wait_for(task_completion_notification, timeout=30.0) - - if found_task.status == TaskInfoStatus.IDLE: - cancel = await session.rpc.tasks.cancel(TasksCancelRequest(id=task_id)) - assert cancel.cancelled is True - - # Remove the task - remove = await session.rpc.tasks.remove(TasksRemoveRequest(id=task_id)) - assert remove.removed is True + if found_task is not None: + assert "TASK_AGENT_DONE" in (found_task.latest_response or found_task.result or "") + + if found_task.status == TaskInfoStatus.IDLE: + cancel = await session.rpc.tasks.cancel(TasksCancelRequest(id=task_id)) + assert cancel.cancelled is True + + remove = await session.rpc.tasks.remove(TasksRemoveRequest(id=task_id)) + # Completion delivery also removes finished tasks, so this call may lose that race. + assert remove.removed or task_completion_notification.done(), ( + f"Task {task_id} was not removed before its completion " + "notification was delivered" + ) after_remove = await session.rpc.tasks.list() - assert not any(t.id == task_id for t in (after_remove.tasks or [])) + task_after_remove = next( + (task for task in (after_remove.tasks or []) if task.id == task_id), + None, + ) + assert task_after_remove is None + + await asyncio.wait_for(task_completion_notification, timeout=30.0) finally: unsubscribe() await session.disconnect() From 6287d1ad4c304ab323466e6da032ad7341da7f22 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:45:00 +0000 Subject: [PATCH 05/72] Update @github/copilot to 1.0.77 (#2183) * Update @github/copilot to 1.0.77 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix Java model coverage test Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ed3b1020-f286-4e71-8fa2-1ebe7579854e --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Stephen Toub Copilot-Session: ed3b1020-f286-4e71-8fa2-1ebe7579854e --- dotnet/src/Generated/Rpc.cs | 18 ++--- dotnet/src/Generated/SessionEvents.cs | 42 ++++++++++- go/rpc/zrpc.go | 28 ++++++-- go/rpc/zsession_events.go | 19 ++++- go/zsession_events.go | 2 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +++++++++---------- java/scripts/codegen/package.json | 2 +- .../github/copilot/generated/AbortReason.java | 4 +- .../generated/AssistantUsageEvent.java | 8 +++ .../copilot/generated/McpServerStatus.java | 4 +- .../generated/McpServersLoadedServer.java | 2 +- .../SessionMcpServerStatusChangedEvent.java | 2 +- .../ToolExecutionStartShellToolInfo.java | 4 +- .../copilot/generated/rpc/AbortReason.java | 4 +- .../copilot/generated/rpc/McpServer.java | 2 +- .../generated/rpc/McpServerStatus.java | 4 +- .../github/copilot/generated/rpc/Model.java | 2 - .../rpc/SessionModelSwitchToParams.java | 2 +- .../generated/rpc/SessionOpenOptions.java | 2 +- .../rpc/SessionOptionsUpdateParams.java | 2 +- .../rpc/GeneratedRpcRecordsCoverageTest.java | 2 +- nodejs/package-lock.json | 72 +++++++++---------- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 11 ++- nodejs/src/generated/session-events.ts | 40 ++++++++++- python/copilot/generated/rpc.py | 37 +++++----- python/copilot/generated/session_events.py | 36 +++++++++- rust/src/generated/api_types.rs | 13 ++-- rust/src/generated/session_events.rs | 38 +++++++++- test/harness/package-lock.json | 72 +++++++++---------- test/harness/package.json | 2 +- 33 files changed, 370 insertions(+), 184 deletions(-) diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index a10efe557..300152099 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -299,10 +299,6 @@ public sealed class Model [JsonPropertyName("capabilities")] public ModelCapabilities Capabilities { get => field ??= new(); set; } - ///

Default reasoning effort level (only present if model supports reasoning effort). - [JsonPropertyName("defaultReasoningEffort")] - public string? DefaultReasoningEffort { get; set; } - /// Model identifier (e.g., "claude-sonnet-4.5"). [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; @@ -491,6 +487,10 @@ public sealed class CopilotUserResponseEndpoints [JsonPropertyName("api")] public string? Api { get; set; } + /// Gets or sets the exp value. + [JsonPropertyName("exp")] + public string? Exp { get; set; } + /// Gets or sets the origin-tracker value. [JsonPropertyName("origin-tracker")] public string? OriginTracker { get; set; } @@ -5176,7 +5176,7 @@ internal sealed class ModelSwitchToRequest [JsonPropertyName("modelId")] public string ModelId { get; set; } = string.Empty; - /// Reasoning effort level to use for the model. "none" disables reasoning. + /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. When omitted, no effort override is applied. [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } @@ -6873,7 +6873,7 @@ public sealed class McpServer [JsonPropertyName("sourcePluginVersion")] public string? SourcePluginVersion { get; set; } - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured. + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. [JsonPropertyName("status")] public McpServerStatus Status { get; set; } } @@ -8854,7 +8854,7 @@ internal sealed class SessionUpdateOptionsParams [JsonPropertyName("provider")] public ProviderConfig? Provider { get; set; } - /// Reasoning effort for the selected model (model-defined enum). + /// Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } @@ -24645,7 +24645,7 @@ public async Task GetCurrentAsync(CancellationToken cancellationTo /// Switches the session to a model and optional reasoning configuration. /// Model selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. - /// Reasoning effort level to use for the model. "none" disables reasoning. + /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. When omitted, no effort override is applied. /// Reasoning summary mode to request for supported model clients. /// Output verbosity level to request for supported models. /// Override individual model capabilities resolved by the runtime. @@ -26010,7 +26010,7 @@ internal OptionsApi(CopilotSession session) /// Patches the genuinely-mutable subset of session options. /// The model ID to use for assistant turns. /// Per-property model capability overrides for the selected model. - /// Reasoning effort for the selected model (model-defined enum). + /// Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. /// Reasoning summary mode for supported model clients. /// Output verbosity level for supported models. /// Identifier of the client driving the session. diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index b66193247..6266daeec 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -2938,6 +2938,12 @@ public sealed partial class AssistantUsageData [JsonPropertyName("apiEndpoint")] public AssistantUsageApiEndpoint? ApiEndpoint { get; set; } + /// Number of tools available to the model for this call. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("availableToolCount")] + internal long? AvailableToolCount { get; set; } + /// Updated prompt-cache expiration for this model call. Present only when the call establishes or refreshes known cache state. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("cacheExpiresAt")] @@ -3005,6 +3011,12 @@ public sealed partial class AssistantUsageData [JsonPropertyName("model")] public required string Model { get; set; } + /// Number of tool calls returned by the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("numToolCalls")] + internal long? NumToolCalls { get; set; } + /// Number of output tokens produced. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("outputTokens")] @@ -3055,6 +3067,18 @@ public sealed partial class AssistantUsageData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("timeToFirstTokenMs")] public TimeSpan? TimeToFirstToken { get; set; } + + /// Tool-call counts keyed by tool name. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("toolCounts")] + internal IDictionary? ToolCounts { get; set; } + + /// Number of tokens used by tool definitions for this call. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("toolTokenCount")] + internal long? ToolTokenCount { get; set; } } /// Failed LLM API call metadata for telemetry. @@ -4416,7 +4440,7 @@ public sealed partial class SessionMcpServerStatusChangedData [JsonPropertyName("serverName")] public required string ServerName { get; set; } - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured. + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. [JsonPropertyName("status")] public required McpServerStatus Status { get; set; } } @@ -5836,6 +5860,12 @@ public sealed partial class ModelCallFailureRequestFingerprint /// Nested data type for ToolExecutionStartShellToolInfo. public sealed partial class ToolExecutionStartShellToolInfo { + /// The command with a redundant leading `cd` into the working directory removed, present only when there was one to remove. Computed with the same routine the shell driver applies before spawning, so a surface that renders this shows the text that actually runs. Consumers that display it should keep the original tool arguments available on demand. + [Experimental(Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayCommand")] + public string? DisplayCommand { get; set; } + /// Whether the command includes a file write redirection (e.g., > or >>). [JsonPropertyName("hasWriteFileRedirection")] public required bool HasWriteFileRedirection { get; set; } @@ -8331,7 +8361,7 @@ public sealed partial class McpServersLoadedServer [JsonPropertyName("source")] public McpServerSource? Source { get; set; } - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured. + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. [JsonPropertyName("status")] public required McpServerStatus Status { get; set; } @@ -10136,6 +10166,9 @@ public AbortReason(string value) /// An MCP server delivered a user.abort notification. public static AbortReason UserAbort { get; } = new("user_abort"); + /// Autopilot stopped the run because the active objective reached its user-set --max-ai-credits limit. + public static AbortReason AutopilotCreditLimit { get; } = new("autopilot_credit_limit"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(AbortReason left, AbortReason right) => left.Equals(right); @@ -12088,7 +12121,7 @@ public override void Write(Utf8JsonWriter writer, McpServerSource value, JsonSer } } -/// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured. +/// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct McpServerStatus : IEquatable @@ -12122,6 +12155,9 @@ public McpServerStatus(string value) /// The server is configured but disabled. public static McpServerStatus Disabled { get; } = new("disabled"); + /// The server was intentionally stopped and can be restarted on demand when policy permits; a server quarantined by restrictive managed policy stays stopped and cannot be restarted until the policy allows it. + public static McpServerStatus Stopped { get; } = new("stopped"); + /// The server is not configured for this session. public static McpServerStatus NotConfigured { get; } = new("not_configured"); diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 341eb6f61..15b7c0cd6 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -1538,6 +1538,7 @@ type CopilotUserResponse struct { // or be removed. type CopilotUserResponseEndpoints struct { API *string `json:"api,omitempty"` + Exp *string `json:"exp,omitempty"` OriginTracker *string `json:"origin-tracker,omitempty"` Proxy *string `json:"proxy,omitempty"` Telemetry *string `json:"telemetry,omitempty"` @@ -4550,7 +4551,8 @@ type MCPServer struct { SourcePlugin *string `json:"sourcePlugin,omitempty"` // Plugin version that provided this server, when source is plugin. SourcePluginVersion *string `json:"sourcePluginVersion,omitempty"` - // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + // Connection status: connected, failed, needs-auth, pending, disabled, stopped, or + // not_configured Status MCPServerStatus `json:"status"` } @@ -4952,8 +4954,6 @@ type Model struct { Billing *ModelBilling `json:"billing,omitempty"` // Model capabilities and limits Capabilities ModelCapabilities `json:"capabilities"` - // Default reasoning effort level (only present if model supports reasoning effort) - DefaultReasoningEffort *string `json:"defaultReasoningEffort,omitempty"` // Model identifier (e.g., "claude-sonnet-4.5") ID string `json:"id"` // Model capability category for grouping in the model picker @@ -5220,7 +5220,9 @@ type ModelSwitchToRequest struct { // `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id // (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. ModelID string `json:"modelId"` - // Reasoning effort level to use for the model. "none" disables reasoning. + // Reasoning effort level to use for the model. CAPI values are model-defined and validated + // against the selected model; BYOK providers may define additional values. "none" disables + // reasoning. When omitted, no effort override is applied. ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Reasoning summary mode to request for supported model clients ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` @@ -9493,7 +9495,9 @@ type SessionOpenOptions struct { // rejected. // Experimental: Providers is part of an experimental API and may change or be removed. Providers []NamedProviderConfig `json:"providers,omitzero"` - // Initial reasoning effort level. + // Initial reasoning effort level. CAPI values are model-defined and validated against the + // selected model; BYOK providers may define additional values. When omitted, no effort + // override is applied. ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Initial reasoning summary mode for supported model clients. ReasoningSummary *SessionOpenOptionsReasoningSummary `json:"reasoningSummary,omitempty"` @@ -10533,7 +10537,9 @@ type SessionUpdateOptionsParams struct { OrganizationCustomInstructions *string `json:"organizationCustomInstructions,omitempty"` // Custom model-provider configuration (BYOK). Provider *ProviderConfig `json:"provider,omitempty"` - // Reasoning effort for the selected model (model-defined enum). + // Reasoning effort for the selected model. CAPI values are model-defined and validated + // against the selected model; BYOK providers may define additional values. When omitted, no + // effort override is applied. ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Reasoning summary mode for supported model clients. ReasoningSummary *OptionsUpdateReasoningSummary `json:"reasoningSummary,omitempty"` @@ -12542,6 +12548,9 @@ type WorkspacesWriteAutopilotObjectiveResult struct { type AbortReason string const ( + // Autopilot stopped the run because the active objective reached its user-set + // --max-ai-credits limit. + AbortReasonAutopilotCreditLimit AbortReason = "autopilot_credit_limit" // A remote command requested the abort. AbortReasonRemoteCommand AbortReason = "remote_command" // An MCP server delivered a user.abort notification. @@ -13573,7 +13582,8 @@ const ( MCPServerSourceWorkspace MCPServerSource = "workspace" ) -// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured +// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or +// not_configured // Experimental: MCPServerStatus is part of an experimental API and may change or be removed. type MCPServerStatus string @@ -13590,6 +13600,10 @@ const ( MCPServerStatusNotConfigured MCPServerStatus = "not_configured" // The server connection is still being established. MCPServerStatusPending MCPServerStatus = "pending" + // The server was intentionally stopped and can be restarted on demand when policy permits; + // a server quarantined by restrictive managed policy stays stopped and cannot be restarted + // until the policy allows it. + MCPServerStatusStopped MCPServerStatus = "stopped" ) // How environment-variable values supplied to MCP servers are resolved. "direct" passes diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index b340b84b2..54b46e25a 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -844,6 +844,9 @@ type AssistantUsageData struct { APICallID *string `json:"apiCallId,omitempty"` // API endpoint used for this model call, matching CAPI supported_endpoints vocabulary APIEndpoint *AssistantUsageAPIEndpoint `json:"apiEndpoint,omitempty"` + // Number of tools available to the model for this call + // Internal: AvailableToolCount is part of the SDK's internal API surface and is not intended for external use. + AvailableToolCount *int64 `json:"availableToolCount,omitempty"` // Updated prompt-cache expiration for this model call. Present only when the call establishes or refreshes known cache state. CacheExpiresAt *time.Time `json:"cacheExpiresAt,omitempty"` // Number of tokens read from prompt cache @@ -871,6 +874,9 @@ type AssistantUsageData struct { InterTokenLatencyMs *float64 `json:"interTokenLatencyMs,omitempty"` // Model identifier used for this API call Model string `json:"model"` + // Number of tool calls returned by the model + // Internal: NumToolCalls is part of the SDK's internal API surface and is not intended for external use. + NumToolCalls *int64 `json:"numToolCalls,omitempty"` // Number of output tokens produced OutputTokens *int64 `json:"outputTokens,omitempty"` // Parent tool call ID when this usage originates from a sub-agent @@ -890,6 +896,12 @@ type AssistantUsageData struct { ServiceRequestID *string `json:"serviceRequestId,omitempty"` // Time to first token in milliseconds. Only available for streaming requests TimeToFirstTokenMs *float64 `json:"timeToFirstTokenMs,omitempty"` + // Tool-call counts keyed by tool name + // Internal: ToolCounts is part of the SDK's internal API surface and is not intended for external use. + ToolCounts map[string]int64 `json:"toolCounts,omitzero"` + // Number of tokens used by tool definitions for this call + // Internal: ToolTokenCount is part of the SDK's internal API surface and is not intended for external use. + ToolTokenCount *int64 `json:"toolTokenCount,omitempty"` } func (*AssistantUsageData) sessionEventData() {} @@ -1210,7 +1222,7 @@ type SessionMCPServerStatusChangedData struct { Error *string `json:"error,omitempty"` // Name of the MCP server whose status changed ServerName string `json:"serverName"` - // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + // Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured Status MCPServerStatus `json:"status"` } @@ -2608,7 +2620,7 @@ type MCPServersLoadedServer struct { PluginVersion *string `json:"pluginVersion,omitempty"` // Configuration source: user, workspace, plugin, or builtin Source *MCPServerSource `json:"source,omitempty"` - // Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + // Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured Status MCPServerStatus `json:"status"` // Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) Transport *MCPServerTransport `json:"transport,omitempty"` @@ -3849,6 +3861,9 @@ type ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone struct { // Shell-aware path hints for a shell tool's command, captured at start time so consumers can snapshot a file's pre-image before the tool runs. type ToolExecutionStartShellToolInfo struct { + // The command with a redundant leading `cd` into the working directory removed, present only when there was one to remove. Computed with the same routine the shell driver applies before spawning, so a surface that renders this shows the text that actually runs. Consumers that display it should keep the original tool arguments available on demand. + // Experimental: DisplayCommand is part of an experimental API and may change or be removed. + DisplayCommand *string `json:"displayCommand,omitempty"` // Whether the command includes a file write redirection (e.g., > or >>). HasWriteFileRedirection bool `json:"hasWriteFileRedirection"` // File paths the command may read or write, derived from the command at start time. Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. diff --git a/go/zsession_events.go b/go/zsession_events.go index c98ddc704..c79814c9e 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -366,6 +366,7 @@ type ( // Session-event constants are generated in the rpc package and re-exported here for source compatibility. const ( + AbortReasonAutopilotCreditLimit = rpc.AbortReasonAutopilotCreditLimit AbortReasonRemoteCommand = rpc.AbortReasonRemoteCommand AbortReasonUserAbort = rpc.AbortReasonUserAbort AbortReasonUserInitiated = rpc.AbortReasonUserInitiated @@ -484,6 +485,7 @@ const ( MCPServerStatusNeedsAuth = rpc.MCPServerStatusNeedsAuth MCPServerStatusNotConfigured = rpc.MCPServerStatusNotConfigured MCPServerStatusPending = rpc.MCPServerStatusPending + MCPServerStatusStopped = rpc.MCPServerStatusStopped MCPServerTransportHTTP = rpc.MCPServerTransportHTTP MCPServerTransportMemory = rpc.MCPServerTransportMemory MCPServerTransportSSE = rpc.MCPServerTransportSSE diff --git a/java/pom.xml b/java/pom.xml index 96770084b..24765bad5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.76-5 + ^1.0.77 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index f751df8fd..3ddbf772b 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.76-5.tgz", - "integrity": "sha512-hbE+dgYeR59lEGTNFgBi7G2xbMNetzq2G9FFrLVD1k0Z1ZavSeZAfrfAuBbVCXvYjr2cfKE6oSeMYUB0fArFjw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", + "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.76-5", - "@github/copilot-darwin-x64": "1.0.76-5", - "@github/copilot-linux-arm64": "1.0.76-5", - "@github/copilot-linux-x64": "1.0.76-5", - "@github/copilot-linuxmusl-arm64": "1.0.76-5", - "@github/copilot-linuxmusl-x64": "1.0.76-5", - "@github/copilot-win32-arm64": "1.0.76-5", - "@github/copilot-win32-x64": "1.0.76-5" + "@github/copilot-darwin-arm64": "1.0.77", + "@github/copilot-darwin-x64": "1.0.77", + "@github/copilot-linux-arm64": "1.0.77", + "@github/copilot-linux-x64": "1.0.77", + "@github/copilot-linuxmusl-arm64": "1.0.77", + "@github/copilot-linuxmusl-x64": "1.0.77", + "@github/copilot-win32-arm64": "1.0.77", + "@github/copilot-win32-x64": "1.0.77" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.76-5.tgz", - "integrity": "sha512-kHg7wUP1RJoNHAd520I1g+nE0kHl4bxQtufg7CE18Xa0aNgC10PcSe9BwqcFEe8RiGdMCUMtohGMKJIqKdyv8Q==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", + "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.76-5.tgz", - "integrity": "sha512-B01l/9qLn+BI3yjsTn7tPHJaCA+2hM6KrqSF7NR5HgQ+AtwRriC+ufwlbWPUatrwtXneQZIRgaSKCNUwLsBrBw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", + "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.76-5.tgz", - "integrity": "sha512-QvZSCcIA5cxOhWtviLAMHHn8em7PNSTM9DMujfgAAGKl9w1AFhbN+hsTOtvOojdZYXVMZuiQlYmtZ8EemyiZOg==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", + "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.76-5.tgz", - "integrity": "sha512-61wmKXleon7KGv7e2WtbUC6oYH/QkpjowTI3YvgriNaey8/SaZo4ODt59D/L47s8PqqpvPCbe/f6zmjGLNNWEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", + "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.76-5.tgz", - "integrity": "sha512-WUD8c5YFEL3LecdocT+VIj83EqnNKrb3YL7Wqk+FtFzzjNmF3UGrVo1CbMerTuQzWU65QhgIt1mEBKN1bMSJqQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", + "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.76-5.tgz", - "integrity": "sha512-HM44XEx1TcZfpLDcFPf0ya0Bi/0wxJzrrR7FRhRG6C2VYBr/mfUsXBUF9V7vZZMv7yKDPro27PLjpsuMDJyYsQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", + "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.76-5.tgz", - "integrity": "sha512-cbrnDRo7cYAbJuCt4f+1Cw8SiFfNOkCBg3RR/jAcV7Y5ED3QEEoad3WN8avFaiAOGHyTJYqBRTQJNBkyRTmrEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", + "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.76-5.tgz", - "integrity": "sha512-ShgpTm07YqQQuRLgJ5BqVzKfSlZ4YMmlz7rl/Br5Ifkz57+/HUF2EJBX2LDt05ha1isOaN1ekZQmWvdzWrVCoQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", + "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index df1e74ac5..f3a144cb1 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/AbortReason.java b/java/src/generated/java/com/github/copilot/generated/AbortReason.java index 2ffbdb8d8..c1ba2119a 100644 --- a/java/src/generated/java/com/github/copilot/generated/AbortReason.java +++ b/java/src/generated/java/com/github/copilot/generated/AbortReason.java @@ -21,7 +21,9 @@ public enum AbortReason { /** The {@code remote_command} variant. */ REMOTE_COMMAND("remote_command"), /** The {@code user_abort} variant. */ - USER_ABORT("user_abort"); + USER_ABORT("user_abort"), + /** The {@code autopilot_credit_limit} variant. */ + AUTOPILOT_CREDIT_LIMIT("autopilot_credit_limit"); private final String value; AbortReason(String value) { this.value = value; } diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java b/java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java index a7de95bb4..85bd81d3c 100644 --- a/java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java @@ -79,6 +79,14 @@ public record AssistantUsageEventData( @JsonProperty("copilotUsage") AssistantUsageCopilotUsage copilotUsage, /** Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") */ @JsonProperty("reasoningEffort") String reasoningEffort, + /** Number of tools available to the model for this call */ + @JsonProperty("availableToolCount") Long availableToolCount, + /** Number of tokens used by tool definitions for this call */ + @JsonProperty("toolTokenCount") Long toolTokenCount, + /** Number of tool calls returned by the model */ + @JsonProperty("numToolCalls") Long numToolCalls, + /** Tool-call counts keyed by tool name */ + @JsonProperty("toolCounts") Map toolCounts, /** Finish reason reported by the model for this API call (e.g. "stop", "length", "tool_calls", "content_filter"). Normalized to OpenAI vocabulary; for Anthropic models a "refusal" stop reason maps to "content_filter". */ @JsonProperty("finishReason") String finishReason, /** Whether the model response was blocked or truncated by content filtering (finish_reason === 'content_filter'). For Anthropic models this corresponds to a 'refusal' stop reason. */ diff --git a/java/src/generated/java/com/github/copilot/generated/McpServerStatus.java b/java/src/generated/java/com/github/copilot/generated/McpServerStatus.java index b5bb08093..f11cebdc9 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpServerStatus.java +++ b/java/src/generated/java/com/github/copilot/generated/McpServerStatus.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + * Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured * * @since 1.0.0 */ @@ -26,6 +26,8 @@ public enum McpServerStatus { PENDING("pending"), /** The {@code disabled} variant. */ DISABLED("disabled"), + /** The {@code stopped} variant. */ + STOPPED("stopped"), /** The {@code not_configured} variant. */ NOT_CONFIGURED("not_configured"); diff --git a/java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java b/java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java index 1a2f05023..c4567f30f 100644 --- a/java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java +++ b/java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java @@ -23,7 +23,7 @@ public record McpServersLoadedServer( /** Server name (config key) */ @JsonProperty("name") String name, - /** Connection status: connected, failed, needs-auth, pending, disabled, or not_configured */ + /** Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured */ @JsonProperty("status") McpServerStatus status, /** Configuration source: user, workspace, plugin, or builtin */ @JsonProperty("source") McpServerSource source, diff --git a/java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java index cb15f1d9e..b084652db 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java @@ -36,7 +36,7 @@ public final class SessionMcpServerStatusChangedEvent extends SessionEvent { public record SessionMcpServerStatusChangedEventData( /** Name of the MCP server whose status changed */ @JsonProperty("serverName") String serverName, - /** Connection status: connected, failed, needs-auth, pending, disabled, or not_configured */ + /** Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured */ @JsonProperty("status") McpServerStatus status, /** Error message if the server entered a failed state */ @JsonProperty("error") String error diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java b/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java index 88ac787fc..967dab4c3 100644 --- a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java +++ b/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java @@ -25,6 +25,8 @@ public record ToolExecutionStartShellToolInfo( /** File paths the command may read or write, derived from the command at start time. Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. */ @JsonProperty("possiblePaths") List possiblePaths, /** Whether the command includes a file write redirection (e.g., > or >>). */ - @JsonProperty("hasWriteFileRedirection") Boolean hasWriteFileRedirection + @JsonProperty("hasWriteFileRedirection") Boolean hasWriteFileRedirection, + /** The command with a redundant leading `cd` into the working directory removed, present only when there was one to remove. Computed with the same routine the shell driver applies before spawning, so a surface that renders this shows the text that actually runs. Consumers that display it should keep the original tool arguments available on demand. */ + @JsonProperty("displayCommand") String displayCommand ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java b/java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java index a48640077..8d26959e8 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java @@ -21,7 +21,9 @@ public enum AbortReason { /** The {@code remote_command} variant. */ REMOTE_COMMAND("remote_command"), /** The {@code user_abort} variant. */ - USER_ABORT("user_abort"); + USER_ABORT("user_abort"), + /** The {@code autopilot_credit_limit} variant. */ + AUTOPILOT_CREDIT_LIMIT("autopilot_credit_limit"); private final String value; AbortReason(String value) { this.value = value; } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java index 382c54c40..14a9118d0 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java @@ -23,7 +23,7 @@ public record McpServer( /** Server name (config key) */ @JsonProperty("name") String name, - /** Connection status: connected, failed, needs-auth, pending, disabled, or not_configured */ + /** Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured */ @JsonProperty("status") McpServerStatus status, /** Configuration source: user, workspace, plugin, or builtin */ @JsonProperty("source") McpServerSource source, diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java b/java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java index db463a737..4c1fb46b2 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + * Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured * * @since 1.0.0 */ @@ -26,6 +26,8 @@ public enum McpServerStatus { PENDING("pending"), /** The {@code disabled} variant. */ DISABLED("disabled"), + /** The {@code stopped} variant. */ + STOPPED("stopped"), /** The {@code not_configured} variant. */ NOT_CONFIGURED("not_configured"); diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Model.java b/java/src/generated/java/com/github/copilot/generated/rpc/Model.java index c55fc026f..f002df540 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/Model.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/Model.java @@ -34,8 +34,6 @@ public record Model( @JsonProperty("billing") ModelBilling billing, /** Supported reasoning effort levels (only present if model supports reasoning effort) */ @JsonProperty("supportedReasoningEfforts") List supportedReasoningEfforts, - /** Default reasoning effort level (only present if model supports reasoning effort) */ - @JsonProperty("defaultReasoningEffort") String defaultReasoningEffort, /** Model capability category for grouping in the model picker */ @JsonProperty("modelPickerCategory") ModelPickerCategory modelPickerCategory, /** Relative cost tier for token-based billing users */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java index a46e989bd..fe49e2976 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java @@ -28,7 +28,7 @@ public record SessionModelSwitchToParams( @JsonProperty("sessionId") String sessionId, /** Model selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. */ @JsonProperty("modelId") String modelId, - /** Reasoning effort level to use for the model. "none" disables reasoning. */ + /** Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. When omitted, no effort override is applied. */ @JsonProperty("reasoningEffort") String reasoningEffort, /** Reasoning summary mode to request for supported model clients */ @JsonProperty("reasoningSummary") ReasoningSummary reasoningSummary, diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java index 10fa26ff3..a8108e829 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java @@ -29,7 +29,7 @@ public record SessionOpenOptions( @JsonProperty("name") String name, /** Initial model identifier. */ @JsonProperty("model") String model, - /** Initial reasoning effort level. */ + /** Initial reasoning effort level. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. */ @JsonProperty("reasoningEffort") String reasoningEffort, /** Initial reasoning summary mode for supported model clients. */ @JsonProperty("reasoningSummary") SessionOpenOptionsReasoningSummary reasoningSummary, diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java index 34ab8c272..080b47866 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java @@ -32,7 +32,7 @@ public record SessionOptionsUpdateParams( @JsonProperty("model") String model, /** Per-property model capability overrides for the selected model. */ @JsonProperty("modelCapabilitiesOverrides") ModelCapabilitiesOverride modelCapabilitiesOverrides, - /** Reasoning effort for the selected model (model-defined enum). */ + /** Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. */ @JsonProperty("reasoningEffort") String reasoningEffort, /** Reasoning summary mode for supported model clients. */ @JsonProperty("reasoningSummary") OptionsUpdateReasoningSummary reasoningSummary, diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 1b1cccaba..3b8a716c7 100644 --- a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -815,7 +815,7 @@ void modelsListResult_nested() { var policy = new ModelPolicy(ModelPolicyState.ENABLED, null); var promo = new ModelBillingPromo("summer-2026", 25.0, "2026-08-01T00:00:00Z", "Summer discount"); var billing = new ModelBilling(1.0, null, null, promo); - var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null, null, null); + var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null, null); var result = new ModelsListResult(List.of(modelItem)); assertEquals(1, result.models().size()); diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 8554f085e..42110e80e 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.76-5.tgz", - "integrity": "sha512-hbE+dgYeR59lEGTNFgBi7G2xbMNetzq2G9FFrLVD1k0Z1ZavSeZAfrfAuBbVCXvYjr2cfKE6oSeMYUB0fArFjw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", + "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.76-5", - "@github/copilot-darwin-x64": "1.0.76-5", - "@github/copilot-linux-arm64": "1.0.76-5", - "@github/copilot-linux-x64": "1.0.76-5", - "@github/copilot-linuxmusl-arm64": "1.0.76-5", - "@github/copilot-linuxmusl-x64": "1.0.76-5", - "@github/copilot-win32-arm64": "1.0.76-5", - "@github/copilot-win32-x64": "1.0.76-5" + "@github/copilot-darwin-arm64": "1.0.77", + "@github/copilot-darwin-x64": "1.0.77", + "@github/copilot-linux-arm64": "1.0.77", + "@github/copilot-linux-x64": "1.0.77", + "@github/copilot-linuxmusl-arm64": "1.0.77", + "@github/copilot-linuxmusl-x64": "1.0.77", + "@github/copilot-win32-arm64": "1.0.77", + "@github/copilot-win32-x64": "1.0.77" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.76-5.tgz", - "integrity": "sha512-kHg7wUP1RJoNHAd520I1g+nE0kHl4bxQtufg7CE18Xa0aNgC10PcSe9BwqcFEe8RiGdMCUMtohGMKJIqKdyv8Q==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", + "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.76-5.tgz", - "integrity": "sha512-B01l/9qLn+BI3yjsTn7tPHJaCA+2hM6KrqSF7NR5HgQ+AtwRriC+ufwlbWPUatrwtXneQZIRgaSKCNUwLsBrBw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", + "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.76-5.tgz", - "integrity": "sha512-QvZSCcIA5cxOhWtviLAMHHn8em7PNSTM9DMujfgAAGKl9w1AFhbN+hsTOtvOojdZYXVMZuiQlYmtZ8EemyiZOg==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", + "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.76-5.tgz", - "integrity": "sha512-61wmKXleon7KGv7e2WtbUC6oYH/QkpjowTI3YvgriNaey8/SaZo4ODt59D/L47s8PqqpvPCbe/f6zmjGLNNWEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", + "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.76-5.tgz", - "integrity": "sha512-WUD8c5YFEL3LecdocT+VIj83EqnNKrb3YL7Wqk+FtFzzjNmF3UGrVo1CbMerTuQzWU65QhgIt1mEBKN1bMSJqQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", + "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.76-5.tgz", - "integrity": "sha512-HM44XEx1TcZfpLDcFPf0ya0Bi/0wxJzrrR7FRhRG6C2VYBr/mfUsXBUF9V7vZZMv7yKDPro27PLjpsuMDJyYsQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", + "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.76-5.tgz", - "integrity": "sha512-cbrnDRo7cYAbJuCt4f+1Cw8SiFfNOkCBg3RR/jAcV7Y5ED3QEEoad3WN8avFaiAOGHyTJYqBRTQJNBkyRTmrEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", + "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.76-5.tgz", - "integrity": "sha512-ShgpTm07YqQQuRLgJ5BqVzKfSlZ4YMmlz7rl/Br5Ifkz57+/HUF2EJBX2LDt05ha1isOaN1ekZQmWvdzWrVCoQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", + "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 56524ef57..8dbe3ac81 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 38feed71f..9e6de6ebe 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index d80c9386b..1cedf34cc 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -2876,6 +2876,7 @@ export interface CopilotUserResponseEndpoints { "origin-tracker"?: string; proxy?: string; telemetry?: string; + exp?: string; } /** * Quota snapshot map from the raw Copilot user-response passthrough, with chat, completions, premium-interactions, and other entries. @@ -9025,10 +9026,6 @@ export interface Model { * Supported reasoning effort levels (only present if model supports reasoning effort) */ supportedReasoningEfforts?: string[]; - /** - * Default reasoning effort level (only present if model supports reasoning effort) - */ - defaultReasoningEffort?: string; modelPickerCategory?: ModelPickerCategory; modelPickerPriceCategory?: ModelPickerPriceCategory; } @@ -9377,7 +9374,7 @@ export interface ModelSwitchToRequest { */ modelId: string; /** - * Reasoning effort level to use for the model. "none" disables reasoning. + * Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. When omitted, no effort override is applied. */ reasoningEffort?: string; reasoningSummary?: ReasoningSummary; @@ -14347,7 +14344,7 @@ export interface SessionOpenOptions { */ model?: string; /** - * Initial reasoning effort level. + * Initial reasoning effort level. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. */ reasoningEffort?: string; reasoningSummary?: SessionOpenOptionsReasoningSummary; @@ -15623,7 +15620,7 @@ export interface SessionUpdateOptionsParams { model?: string; modelCapabilitiesOverrides?: ModelCapabilitiesOverride; /** - * Reasoning effort for the selected model (model-defined enum). + * Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. */ reasoningEffort?: string; reasoningSummary?: OptionsUpdateReasoningSummary; diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index e619488b2..4e5fb4274 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -402,7 +402,9 @@ export type AbortReason = /** A remote command requested the abort. */ | "remote_command" /** An MCP server delivered a user.abort notification. */ - | "user_abort"; + | "user_abort" + /** Autopilot stopped the run because the active objective reached its user-set --max-ai-credits limit. */ + | "autopilot_credit_limit"; /** * Allowed values for the `ToolExecutionStartToolDescriptionMetaUIVisibility` enumeration. */ @@ -787,7 +789,7 @@ export type McpServerSource = /** Server bundled with the runtime. */ | "builtin"; /** - * Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + * Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured */ export type McpServerStatus = /** The server is connected and available. */ @@ -800,6 +802,8 @@ export type McpServerStatus = | "pending" /** The server is configured but disabled. */ | "disabled" + /** The server was intentionally stopped and can be restarted on demand when policy permits; a server quarantined by restrictive managed policy stays stopped and cannot be restarted until the policy allows it. */ + | "stopped" /** The server is not configured for this session. */ | "not_configured"; /** @@ -4027,6 +4031,12 @@ export interface AssistantUsageData { */ apiCallId?: string; apiEndpoint?: AssistantUsageApiEndpoint; + /** + * Number of tools available to the model for this call + * + * @internal + */ + availableToolCount?: number; /** * Updated prompt-cache expiration for this model call. Present only when the call establishes or refreshes known cache state. */ @@ -4078,6 +4088,12 @@ export interface AssistantUsageData { * Model identifier used for this API call */ model: string; + /** + * Number of tool calls returned by the model + * + * @internal + */ + numToolCalls?: number; /** * Number of output tokens produced */ @@ -4116,6 +4132,20 @@ export interface AssistantUsageData { * Time to first token in milliseconds. Only available for streaming requests */ timeToFirstTokenMs?: number; + /** + * Tool-call counts keyed by tool name + * + * @internal + */ + toolCounts?: { + [k: string]: number | undefined; + }; + /** + * Number of tokens used by tool definitions for this call + * + * @internal + */ + toolTokenCount?: number; } /** * Per-request cost and usage data from the CAPI copilot_usage response field @@ -4534,6 +4564,12 @@ export interface ToolExecutionStartData { * Shell-aware path hints for a shell tool's command, captured at start time so consumers can snapshot a file's pre-image before the tool runs. */ export interface ToolExecutionStartShellToolInfo { + /** + * The command with a redundant leading `cd` into the working directory removed, present only when there was one to remove. Computed with the same routine the shell driver applies before spawning, so a surface that renders this shows the text that actually runs. Consumers that display it should keep the original tool arguments available on demand. + * + * @experimental + */ + displayCommand?: string; /** * Whether the command includes a file write redirection (e.g., > or >>). */ diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 463e6d346..b8c8e395a 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -126,6 +126,7 @@ class CopilotUserResponseEndpoints: """Endpoint URLs from the raw Copilot `/copilot_internal/v2/token` user-response passthrough.""" api: str | None = None + exp: str | None = None origin_tracker: str | None = None proxy: str | None = None telemetry: str | None = None @@ -134,15 +135,18 @@ class CopilotUserResponseEndpoints: def from_dict(obj: Any) -> 'CopilotUserResponseEndpoints': assert isinstance(obj, dict) api = from_union([from_str, from_none], obj.get("api")) + exp = from_union([from_str, from_none], obj.get("exp")) origin_tracker = from_union([from_str, from_none], obj.get("origin-tracker")) proxy = from_union([from_str, from_none], obj.get("proxy")) telemetry = from_union([from_str, from_none], obj.get("telemetry")) - return CopilotUserResponseEndpoints(api, origin_tracker, proxy, telemetry) + return CopilotUserResponseEndpoints(api, exp, origin_tracker, proxy, telemetry) def to_dict(self) -> dict: result: dict = {} if self.api is not None: result["api"] = from_union([from_str, from_none], self.api) + if self.exp is not None: + result["exp"] = from_union([from_str, from_none], self.exp) if self.origin_tracker is not None: result["origin-tracker"] = from_union([from_str, from_none], self.origin_tracker) if self.proxy is not None: @@ -17353,8 +17357,9 @@ class MCPServer: """Server name (config key)""" status: McpServerStatus - """Connection status: connected, failed, needs-auth, pending, disabled, or not_configured""" - + """Connection status: connected, failed, needs-auth, pending, disabled, stopped, or + not_configured + """ error: str | None = None """Error message if the server failed to connect""" @@ -25206,8 +25211,10 @@ class SessionOpenOptions: rejected. """ reasoning_effort: str | None = None - """Initial reasoning effort level.""" - + """Initial reasoning effort level. CAPI values are model-defined and validated against the + selected model; BYOK providers may define additional values. When omitted, no effort + override is applied. + """ reasoning_summary: ReasoningSummary | None = None """Initial reasoning summary mode for supported model clients.""" @@ -25612,8 +25619,10 @@ class SessionUpdateOptionsParams: """Custom model-provider configuration (BYOK).""" reasoning_effort: str | None = None - """Reasoning effort for the selected model (model-defined enum).""" - + """Reasoning effort for the selected model. CAPI values are model-defined and validated + against the selected model; BYOK providers may define additional values. When omitted, no + effort override is applied. + """ reasoning_summary: ReasoningSummary | None = None """Reasoning summary mode for supported model clients.""" @@ -27221,9 +27230,6 @@ class Model: billing: ModelBilling | None = None """Billing information""" - default_reasoning_effort: str | None = None - """Default reasoning effort level (only present if model supports reasoning effort)""" - model_picker_category: ModelPickerCategory | None = None """Model capability category for grouping in the model picker""" @@ -27243,12 +27249,11 @@ def from_dict(obj: Any) -> 'Model': id = from_str(obj.get("id")) name = from_str(obj.get("name")) billing = from_union([ModelBilling.from_dict, from_none], obj.get("billing")) - default_reasoning_effort = from_union([from_str, from_none], obj.get("defaultReasoningEffort")) model_picker_category = from_union([ModelPickerCategory, from_none], obj.get("modelPickerCategory")) model_picker_price_category = from_union([ModelPickerPriceCategory, from_none], obj.get("modelPickerPriceCategory")) policy = from_union([ModelPolicy.from_dict, from_none], obj.get("policy")) supported_reasoning_efforts = from_union([lambda x: from_list(from_str, x), from_none], obj.get("supportedReasoningEfforts")) - return Model(capabilities, id, name, billing, default_reasoning_effort, model_picker_category, model_picker_price_category, policy, supported_reasoning_efforts) + return Model(capabilities, id, name, billing, model_picker_category, model_picker_price_category, policy, supported_reasoning_efforts) def to_dict(self) -> dict: result: dict = {} @@ -27257,8 +27262,6 @@ def to_dict(self) -> dict: result["name"] = from_str(self.name) if self.billing is not None: result["billing"] = from_union([lambda x: to_class(ModelBilling, x), from_none], self.billing) - if self.default_reasoning_effort is not None: - result["defaultReasoningEffort"] = from_union([from_str, from_none], self.default_reasoning_effort) if self.model_picker_category is not None: result["modelPickerCategory"] = from_union([lambda x: to_enum(ModelPickerCategory, x), from_none], self.model_picker_category) if self.model_picker_price_category is not None: @@ -27315,8 +27318,10 @@ class ModelSwitchToRequest: """Override individual model capabilities resolved by the runtime""" reasoning_effort: str | None = None - """Reasoning effort level to use for the model. "none" disables reasoning.""" - + """Reasoning effort level to use for the model. CAPI values are model-defined and validated + against the selected model; BYOK providers may define additional values. "none" disables + reasoning. When omitted, no effort override is applied. + """ reasoning_summary: ReasoningSummary | None = None """Reasoning summary mode to request for supported model clients""" diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index c137c4798..a9f6fe701 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1807,6 +1807,8 @@ class AssistantUsageData: model: str api_call_id: str | None = None api_endpoint: AssistantUsageApiEndpoint | None = None + # Internal: this field is an internal SDK API and is not part of the public surface. + _available_tool_count: int | None = None cache_expires_at: datetime | None = None cache_read_tokens: int | None = None cache_write_tokens: int | None = None @@ -1820,6 +1822,8 @@ class AssistantUsageData: input_tokens: int | None = None interaction_type: str | None = None inter_token_latency: timedelta | None = None + # Internal: this field is an internal SDK API and is not part of the public surface. + _num_tool_calls: int | None = None output_tokens: int | None = None # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @@ -1831,6 +1835,10 @@ class AssistantUsageData: rte: bool | None = None service_request_id: str | None = None time_to_first_token: timedelta | None = None + # Internal: this field is an internal SDK API and is not part of the public surface. + _tool_counts: dict[str, int] | None = None + # Internal: this field is an internal SDK API and is not part of the public surface. + _tool_token_count: int | None = None @staticmethod def from_dict(obj: Any) -> "AssistantUsageData": @@ -1838,6 +1846,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": model = from_str(obj.get("model")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) api_endpoint = from_union([from_none, lambda x: parse_enum(AssistantUsageApiEndpoint, x)], obj.get("apiEndpoint")) + _available_tool_count = from_union([from_none, from_int], obj.get("availableToolCount")) cache_expires_at = from_union([from_none, from_datetime], obj.get("cacheExpiresAt")) cache_read_tokens = from_union([from_none, from_int], obj.get("cacheReadTokens")) cache_write_tokens = from_union([from_none, from_int], obj.get("cacheWriteTokens")) @@ -1850,6 +1859,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": input_tokens = from_union([from_none, from_int], obj.get("inputTokens")) interaction_type = from_union([from_none, from_str], obj.get("interactionType")) inter_token_latency = from_union([from_none, from_timedelta], obj.get("interTokenLatencyMs")) + _num_tool_calls = from_union([from_none, from_int], obj.get("numToolCalls")) output_tokens = from_union([from_none, from_int], obj.get("outputTokens")) parent_tool_call_id = from_union([from_none, from_str], obj.get("parentToolCallId")) provider_call_id = from_union([from_none, from_str], obj.get("providerCallId")) @@ -1859,10 +1869,13 @@ def from_dict(obj: Any) -> "AssistantUsageData": rte = from_union([from_none, from_bool], obj.get("rte")) service_request_id = from_union([from_none, from_str], obj.get("serviceRequestId")) time_to_first_token = from_union([from_none, from_timedelta], obj.get("timeToFirstTokenMs")) + _tool_counts = from_union([from_none, lambda x: from_dict(from_int, x)], obj.get("toolCounts")) + _tool_token_count = from_union([from_none, from_int], obj.get("toolTokenCount")) return AssistantUsageData( model=model, api_call_id=api_call_id, api_endpoint=api_endpoint, + _available_tool_count=_available_tool_count, cache_expires_at=cache_expires_at, cache_read_tokens=cache_read_tokens, cache_write_tokens=cache_write_tokens, @@ -1875,6 +1888,7 @@ def from_dict(obj: Any) -> "AssistantUsageData": input_tokens=input_tokens, interaction_type=interaction_type, inter_token_latency=inter_token_latency, + _num_tool_calls=_num_tool_calls, output_tokens=output_tokens, parent_tool_call_id=parent_tool_call_id, provider_call_id=provider_call_id, @@ -1884,6 +1898,8 @@ def from_dict(obj: Any) -> "AssistantUsageData": rte=rte, service_request_id=service_request_id, time_to_first_token=time_to_first_token, + _tool_counts=_tool_counts, + _tool_token_count=_tool_token_count, ) def to_dict(self) -> dict: @@ -1893,6 +1909,8 @@ def to_dict(self) -> dict: result["apiCallId"] = from_union([from_none, from_str], self.api_call_id) if self.api_endpoint is not None: result["apiEndpoint"] = from_union([from_none, lambda x: to_enum(AssistantUsageApiEndpoint, x)], self.api_endpoint) + if self._available_tool_count is not None: + result["availableToolCount"] = from_union([from_none, to_int], self._available_tool_count) if self.cache_expires_at is not None: result["cacheExpiresAt"] = from_union([from_none, to_datetime], self.cache_expires_at) if self.cache_read_tokens is not None: @@ -1917,6 +1935,8 @@ def to_dict(self) -> dict: result["interactionType"] = from_union([from_none, from_str], self.interaction_type) if self.inter_token_latency is not None: result["interTokenLatencyMs"] = from_union([from_none, to_timedelta], self.inter_token_latency) + if self._num_tool_calls is not None: + result["numToolCalls"] = from_union([from_none, to_int], self._num_tool_calls) if self.output_tokens is not None: result["outputTokens"] = from_union([from_none, to_int], self.output_tokens) if self.parent_tool_call_id is not None: @@ -1935,6 +1955,10 @@ def to_dict(self) -> dict: result["serviceRequestId"] = from_union([from_none, from_str], self.service_request_id) if self.time_to_first_token is not None: result["timeToFirstTokenMs"] = from_union([from_none, to_timedelta], self.time_to_first_token) + if self._tool_counts is not None: + result["toolCounts"] = from_union([from_none, lambda x: from_dict(to_int, x)], self._tool_counts) + if self._tool_token_count is not None: + result["toolTokenCount"] = from_union([from_none, to_int], self._tool_token_count) return result @@ -8775,21 +8799,27 @@ class ToolExecutionStartShellToolInfo: "Shell-aware path hints for a shell tool's command, captured at start time so consumers can snapshot a file's pre-image before the tool runs." has_write_file_redirection: bool possible_paths: list[str] + # Experimental: this field is part of an experimental API and may change or be removed. + display_command: str | None = None @staticmethod def from_dict(obj: Any) -> "ToolExecutionStartShellToolInfo": assert isinstance(obj, dict) has_write_file_redirection = from_bool(obj.get("hasWriteFileRedirection")) possible_paths = from_list(from_str, obj.get("possiblePaths")) + display_command = from_union([from_none, from_str], obj.get("displayCommand")) return ToolExecutionStartShellToolInfo( has_write_file_redirection=has_write_file_redirection, possible_paths=possible_paths, + display_command=display_command, ) def to_dict(self) -> dict: result: dict = {} result["hasWriteFileRedirection"] = from_bool(self.has_write_file_redirection) result["possiblePaths"] = from_list(from_str, self.possible_paths) + if self.display_command is not None: + result["displayCommand"] = from_union([from_none, from_str], self.display_command) return result @@ -9526,6 +9556,8 @@ class AbortReason(Enum): REMOTE_COMMAND = "remote_command" # An MCP server delivered a user.abort notification. USER_ABORT = "user_abort" + # Autopilot stopped the run because the active objective reached its user-set --max-ai-credits limit. + AUTOPILOT_CREDIT_LIMIT = "autopilot_credit_limit" class AssistantMessageToolRequestType(Enum): @@ -9783,7 +9815,7 @@ class McpServerSource(Enum): class McpServerStatus(Enum): - "Connection status: connected, failed, needs-auth, pending, disabled, or not_configured" + "Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured" # The server is connected and available. CONNECTED = "connected" # The server failed to connect or initialize. @@ -9794,6 +9826,8 @@ class McpServerStatus(Enum): PENDING = "pending" # The server is configured but disabled. DISABLED = "disabled" + # The server was intentionally stopped and can be restarted on demand when policy permits; a server quarantined by restrictive managed policy stays stopped and cannot be restarted until the policy allows it. + STOPPED = "stopped" # The server is not configured for this session. NOT_CONFIGURED = "not_configured" diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 909b3b8f5..0472ea0f7 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -1376,6 +1376,8 @@ pub struct AllowAllPermissionState { pub struct CopilotUserResponseEndpoints { #[serde(skip_serializing_if = "Option::is_none")] pub api: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub exp: Option, #[serde(rename = "origin-tracker", skip_serializing_if = "Option::is_none")] pub origin_tracker: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -7176,7 +7178,7 @@ pub struct McpServer { /// Plugin version that provided this server, when source is plugin. #[serde(skip_serializing_if = "Option::is_none")] pub source_plugin_version: Option, - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured pub status: McpServerStatus, } @@ -8017,9 +8019,6 @@ pub struct Model { pub billing: Option, /// Model capabilities and limits pub capabilities: ModelCapabilities, - /// Default reasoning effort level (only present if model supports reasoning effort) - #[serde(skip_serializing_if = "Option::is_none")] - pub default_reasoning_effort: Option, /// Model identifier (e.g., "claude-sonnet-4.5") pub id: String, /// Model capability category for grouping in the model picker @@ -8234,7 +8233,7 @@ pub struct ModelSwitchToRequest { pub model_capabilities: Option, /// Model selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. pub model_id: String, - /// Reasoning effort level to use for the model. "none" disables reasoning. + /// Reasoning effort level to use for the model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. "none" disables reasoning. When omitted, no effort override is applied. #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, /// Reasoning summary mode to request for supported model clients @@ -13958,7 +13957,7 @@ pub struct SessionOpenOptions { /// #[serde(skip_serializing_if = "Option::is_none")] pub providers: Option>, - /// Initial reasoning effort level. + /// Initial reasoning effort level. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, /// Initial reasoning summary mode for supported model clients. @@ -15309,7 +15308,7 @@ pub struct SessionUpdateOptionsParams { /// Custom model-provider configuration (BYOK). #[serde(skip_serializing_if = "Option::is_none")] pub provider: Option, - /// Reasoning effort for the selected model (model-defined enum). + /// Reasoning effort for the selected model. CAPI values are model-defined and validated against the selected model; BYOK providers may define additional values. When omitted, no effort override is applied. #[serde(skip_serializing_if = "Option::is_none")] pub reasoning_effort: Option, /// Reasoning summary mode for supported model clients. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 2aef5765c..7bcba377a 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1986,6 +1986,10 @@ pub struct AssistantUsageData { /// API endpoint used for this model call, matching CAPI supported_endpoints vocabulary #[serde(skip_serializing_if = "Option::is_none")] pub api_endpoint: Option, + /// Number of tools available to the model for this call + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) available_tool_count: Option, /// Updated prompt-cache expiration for this model call. Present only when the call establishes or refreshes known cache state. #[serde(skip_serializing_if = "Option::is_none")] pub cache_expires_at: Option, @@ -2031,6 +2035,10 @@ pub struct AssistantUsageData { pub inter_token_latency_ms: Option, /// Model identifier used for this API call pub model: String, + /// Number of tool calls returned by the model + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) num_tool_calls: Option, /// Number of output tokens produced #[serde(skip_serializing_if = "Option::is_none")] pub output_tokens: Option, @@ -2060,6 +2068,14 @@ pub struct AssistantUsageData { /// Time to first token in milliseconds. Only available for streaming requests #[serde(skip_serializing_if = "Option::is_none")] pub time_to_first_token_ms: Option, + /// Tool-call counts keyed by tool name + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) tool_counts: Option>, + /// Number of tokens used by tool definitions for this call + #[doc(hidden)] + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) tool_token_count: Option, } /// Content-free structural summary of the failing request for diagnosing malformed 4xx calls @@ -2197,6 +2213,16 @@ pub struct ToolUserRequestedData { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ToolExecutionStartShellToolInfo { + /// The command with a redundant leading `cd` into the working directory removed, present only when there was one to remove. Computed with the same routine the shell driver applies before spawning, so a surface that renders this shows the text that actually runs. Consumers that display it should keep the original tool arguments available on demand. + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(skip_serializing_if = "Option::is_none")] + pub display_command: Option, /// Whether the command includes a file write redirection (e.g., > or >>). pub has_write_file_redirection: bool, /// File paths the command may read or write, derived from the command at start time. Produced by the same shell-aware extractor as PermissionRequestShell.possiblePaths, so it is present even when the command is auto-approved and no permission request fires. @@ -4501,7 +4527,7 @@ pub struct McpServersLoadedServer { /// Configuration source: user, workspace, plugin, or builtin #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured pub status: McpServerStatus, /// Transport mechanism: stdio, http, sse (deprecated), or memory (in-process MCP server) #[serde(skip_serializing_if = "Option::is_none")] @@ -4525,7 +4551,7 @@ pub struct SessionMcpServerStatusChangedData { pub error: Option, /// Name of the MCP server whose status changed pub server_name: String, - /// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured pub status: McpServerStatus, } @@ -5269,6 +5295,9 @@ pub enum AbortReason { /// An MCP server delivered a user.abort notification. #[serde(rename = "user_abort")] UserAbort, + /// Autopilot stopped the run because the active objective reached its user-set --max-ai-credits limit. + #[serde(rename = "autopilot_credit_limit")] + AutopilotCreditLimit, /// Unknown variant for forward compatibility. #[default] #[serde(other)] @@ -6224,7 +6253,7 @@ pub enum McpServerSource { Unknown, } -/// Connection status: connected, failed, needs-auth, pending, disabled, or not_configured +/// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum McpServerStatus { /// The server is connected and available. @@ -6242,6 +6271,9 @@ pub enum McpServerStatus { /// The server is configured but disabled. #[serde(rename = "disabled")] Disabled, + /// The server was intentionally stopped and can be restarted on demand when policy permits; a server quarantined by restrictive managed policy stays stopped and cannot be restarted until the policy allows it. + #[serde(rename = "stopped")] + Stopped, /// The server is not configured for this session. #[serde(rename = "not_configured")] NotConfigured, diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 1b414dcad..76b19f6ad 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.76-5.tgz", - "integrity": "sha512-hbE+dgYeR59lEGTNFgBi7G2xbMNetzq2G9FFrLVD1k0Z1ZavSeZAfrfAuBbVCXvYjr2cfKE6oSeMYUB0fArFjw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", + "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.76-5", - "@github/copilot-darwin-x64": "1.0.76-5", - "@github/copilot-linux-arm64": "1.0.76-5", - "@github/copilot-linux-x64": "1.0.76-5", - "@github/copilot-linuxmusl-arm64": "1.0.76-5", - "@github/copilot-linuxmusl-x64": "1.0.76-5", - "@github/copilot-win32-arm64": "1.0.76-5", - "@github/copilot-win32-x64": "1.0.76-5" + "@github/copilot-darwin-arm64": "1.0.77", + "@github/copilot-darwin-x64": "1.0.77", + "@github/copilot-linux-arm64": "1.0.77", + "@github/copilot-linux-x64": "1.0.77", + "@github/copilot-linuxmusl-arm64": "1.0.77", + "@github/copilot-linuxmusl-x64": "1.0.77", + "@github/copilot-win32-arm64": "1.0.77", + "@github/copilot-win32-x64": "1.0.77" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.76-5.tgz", - "integrity": "sha512-kHg7wUP1RJoNHAd520I1g+nE0kHl4bxQtufg7CE18Xa0aNgC10PcSe9BwqcFEe8RiGdMCUMtohGMKJIqKdyv8Q==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", + "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.76-5.tgz", - "integrity": "sha512-B01l/9qLn+BI3yjsTn7tPHJaCA+2hM6KrqSF7NR5HgQ+AtwRriC+ufwlbWPUatrwtXneQZIRgaSKCNUwLsBrBw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", + "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.76-5.tgz", - "integrity": "sha512-QvZSCcIA5cxOhWtviLAMHHn8em7PNSTM9DMujfgAAGKl9w1AFhbN+hsTOtvOojdZYXVMZuiQlYmtZ8EemyiZOg==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", + "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.76-5.tgz", - "integrity": "sha512-61wmKXleon7KGv7e2WtbUC6oYH/QkpjowTI3YvgriNaey8/SaZo4ODt59D/L47s8PqqpvPCbe/f6zmjGLNNWEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", + "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.76-5.tgz", - "integrity": "sha512-WUD8c5YFEL3LecdocT+VIj83EqnNKrb3YL7Wqk+FtFzzjNmF3UGrVo1CbMerTuQzWU65QhgIt1mEBKN1bMSJqQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", + "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.76-5.tgz", - "integrity": "sha512-HM44XEx1TcZfpLDcFPf0ya0Bi/0wxJzrrR7FRhRG6C2VYBr/mfUsXBUF9V7vZZMv7yKDPro27PLjpsuMDJyYsQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", + "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.76-5.tgz", - "integrity": "sha512-cbrnDRo7cYAbJuCt4f+1Cw8SiFfNOkCBg3RR/jAcV7Y5ED3QEEoad3WN8avFaiAOGHyTJYqBRTQJNBkyRTmrEw==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", + "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.76-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.76-5.tgz", - "integrity": "sha512-ShgpTm07YqQQuRLgJ5BqVzKfSlZ4YMmlz7rl/Br5Ifkz57+/HUF2EJBX2LDt05ha1isOaN1ekZQmWvdzWrVCoQ==", + "version": "1.0.77", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", + "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index bd49e5bdd..07b82a4d6 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.76-5", + "@github/copilot": "^1.0.77", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From 72de60fc0fcc203b7ab918ed1ec1bd66e1b59e77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:26:35 -0400 Subject: [PATCH 06/72] Update @github/copilot to 1.0.78-2 (#2193) * Update @github/copilot to 1.0.78-2 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix CI breaks from the CLI 1.0.78-2 schema update The dependency bump regenerated types but left hand-written code and tests behind, breaking every language job. Five independent fixes: - Go: the CLI added a `factory` permission-request kind, so `PermissionRequestFactory` needed a `RequiresManagedApproval()` impl. Added it, and registered the new variant in the codegen shim (`PERMISSION_REQUEST_DEFINITION_NAMES`) so Go/Python/Rust all carry the `managedApprovalRequired` field consistently. - .NET: `session.start` gained `githubMcpToolConfig`, so codegen emitted a `GitHubMcpToolConfig` class colliding with the hand-written one in `dotnet/src/Types.cs` (15 CS0260/CS0102 errors). Taught the C# generator to skip nested classes whose names already exist hand-written under `dotnet/src`, mirroring the existing behavior in the Go generator. The `[JsonSerializable]` registrations are preserved. - Java: `SessionEventHandlingTest` calls generated record constructors positionally; `SessionStartEventData` gained `githubMcpToolConfig` and `AssistantMessageEventData` gained `chunkIndex`/`chunkCount`. Padded the three call sites. This also unblocks CodeQL's java-kotlin analysis. - Rust: `EventLogReadRequest` gained `agent_ids` and `direction`; added them to the three exhaustive struct literals in `tests/e2e/rpc_event_log.rs`. - Node.js: the CLI can now answer `session.factory.run`/`resume` before the run settles, so the e2e test saw `status: "running"`. `SessionFactoryApi` documents these as resolving with a terminal envelope, so both now route through a `settleFactoryRun` helper that waits for terminal state when the initial envelope is non-terminal. Correct under both old and new CLI behavior. Validated locally: go build/vet, dotnet build (src + test), mvn test-compile + spotless:check + SessionEventHandlingTest (29/29), cargo check --tests + cargo fmt --check, npm run typecheck/lint, and the full Node unit suite (363 tests). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 820d6939-ba01-4044-8ee3-c5f5b4d3f443 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Stephen Toub Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 820d6939-ba01-4044-8ee3-c5f5b4d3f443 --- dotnet/src/Generated/Rpc.cs | 474 ++++++++++++++++- dotnet/src/Generated/SessionEvents.cs | 402 ++++++++++++++ go/rpc/permission_request_managed_approval.go | 6 + go/rpc/zrpc.go | 382 +++++++++++++- go/rpc/zrpc_encoding.go | 68 +++ go/rpc/zsession_encoding.go | 51 ++ go/rpc/zsession_events.go | 159 ++++++ go/zsession_events.go | 17 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +-- java/scripts/codegen/package.json | 2 +- .../generated/AssistantMessageEvent.java | 4 + .../generated/GitHubMcpToolConfig.java | 34 ++ .../copilot/generated/SessionStartEvent.java | 2 + .../generated/rpc/DiscoveredExtension.java | 37 ++ .../rpc/DiscoveredExtensionMode.java | 37 ++ .../rpc/DiscoveredExtensionPlugin.java | 27 + .../rpc/DiscoveredExtensionSource.java | 35 ++ .../generated/rpc/EventsCursorStatus.java | 2 +- .../generated/rpc/EventsReadDirection.java | 35 ++ .../rpc/ExtensionsDisableParams.java | 31 ++ .../rpc/ExtensionsDiscoverResult.java | 33 ++ .../generated/rpc/ExtensionsEnableParams.java | 31 ++ .../copilot/generated/rpc/SandboxConfig.java | 4 +- .../generated/rpc/ServerExtensionsApi.java | 62 +++ .../copilot/generated/rpc/ServerRpc.java | 3 + .../generated/rpc/SessionAgentApi.java | 16 + .../rpc/SessionAgentSetPromptParams.java | 34 ++ .../rpc/SessionEventLogReadParams.java | 9 +- .../rpc/SessionEventLogReadResult.java | 8 +- .../generated/rpc/SessionMcpOauthApi.java | 16 + ...OauthAuthenticationStateChangedParams.java | 34 ++ .../SessionPermissionsSetAllowAllParams.java | 2 +- .../copilot/SessionEventHandlingTest.java | 6 +- nodejs/package-lock.json | 72 +-- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 271 +++++++++- nodejs/src/generated/session-events.ts | 246 +++++++++ nodejs/src/session.ts | 18 +- python/copilot/generated/rpc.py | 493 +++++++++++++++++- python/copilot/generated/session_events.py | 384 +++++++++++++- rust/src/generated/api_types.rs | 335 +++++++++++- rust/src/generated/rpc.rs | 147 +++++- rust/src/generated/session_events.rs | 193 +++++++ rust/tests/e2e/rpc_event_log.rs | 6 + scripts/codegen/csharp.ts | 60 ++- scripts/codegen/utils.ts | 1 + test/harness/package-lock.json | 72 +-- test/harness/package.json | 2 +- 50 files changed, 4247 insertions(+), 194 deletions(-) create mode 100644 java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 300152099..dd501e66f 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -1180,6 +1180,75 @@ internal sealed class McpConfigDisableRequest public IList Names { get => field ??= []; set; } } +/// Installed plugin that contributes a discovered extension. +[Experimental(Diagnostics.Experimental)] +public sealed class DiscoveredExtensionPlugin +{ + /// Installed plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} + +/// Discovered extension metadata and persistent enablement state. +[Experimental(Diagnostics.Experimental)] +public sealed class DiscoveredExtension +{ + /// Whether this extension's persistent per-ID preference is enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Source-qualified ID accepted by both server and session extension enablement methods. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Human-readable extension name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Absolute path to the extension entry module, suitable for revealing it in a file manager. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Containing plugin metadata for plugin-contributed extensions. + [JsonPropertyName("plugin")] + public DiscoveredExtensionPlugin? Plugin { get; set; } + + /// Discovery source. + [JsonPropertyName("source")] + public DiscoveredExtensionSource Source { get; set; } +} + +/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +[Experimental(Diagnostics.Experimental)] +public sealed class DiscoveredExtensions +{ + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state. + [JsonPropertyName("extensions")] + public IList Extensions { get => field ??= []; set; } + + /// Effective extension loading mode. Defaults to load_and_augment when unset. + [JsonPropertyName("mode")] + public DiscoveredExtensionMode Mode { get; set; } +} + +/// Source-qualified extension identifiers to persistently enable for future sessions. +[Experimental(Diagnostics.Experimental)] +internal sealed class DiscoveredExtensionsEnableRequest +{ + /// Source-qualified user or plugin extension IDs to enable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } +} + +/// Source-qualified extension identifiers to persistently disable for future sessions. +[Experimental(Diagnostics.Experimental)] +internal sealed class DiscoveredExtensionsDisableRequest +{ + /// Source-qualified user or plugin extension IDs to disable. + [JsonPropertyName("ids")] + public IList Ids { get => field ??= []; set; } +} + /// Information about an installed plugin tracked in global state. [Experimental(Diagnostics.Experimental)] public sealed class InstalledPluginInfo @@ -6093,6 +6162,23 @@ internal sealed class SessionAgentListRequestWithSession public string SessionId { get; set; } = string.Empty; } +/// An in-memory authored prompt override for an available agent. +[Experimental(Diagnostics.Experimental)] +internal sealed class AgentSetPromptRequest +{ + /// Stable effective agent id. Plugin namespace separators are normalized. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Replacement authored prompt. Empty text is valid. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// The currently selected custom agent, or null when using the default agent. [Experimental(Diagnostics.Experimental)] public sealed class AgentGetCurrentResult @@ -7384,6 +7470,23 @@ internal sealed class McpOauthHandlePendingRequest public string SessionId { get; set; } = string.Empty; } +/// Identifies the MCP server whose persisted OAuth credentials were updated. +[Experimental(Diagnostics.Experimental)] +internal sealed class McpOauthAuthenticationStateChangedRequest +{ + /// Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. + [JsonPropertyName("refreshSessionToken")] + public bool? RefreshSessionToken { get; set; } + + /// Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. + [JsonPropertyName("serverName")] + public string? ServerName { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. [Experimental(Diagnostics.Experimental)] public sealed class McpOauthLoginResult @@ -8628,6 +8731,10 @@ public sealed class SandboxConfig [JsonPropertyName("addCurrentWorkingDirectory")] public bool? AddCurrentWorkingDirectory { get; set; } + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("allowDevToolCaches")] + public bool? AllowDevToolCaches { get; set; } + /// Whether sandboxing is enabled for the session. [JsonPropertyName("enabled")] public bool Enabled { get; set; } @@ -10551,6 +10658,7 @@ public partial class PermissionDecisionApproveOnce : PermissionDecision [JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMemory), "memory")] [JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCustomTool), "custom-tool")] [JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalFactory), "factory")] [JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess), "extension-permission-access")] public partial class PermissionDecisionApproveForSessionApproval { @@ -10665,6 +10773,21 @@ public partial class PermissionDecisionApproveForSessionApprovalExtensionManagem public string? Operation { get; set; } } +/// Session-scoped factory approval, optionally narrowed by approval key. +/// The factory variant of . +[Experimental(Diagnostics.Experimental)] +public partial class PermissionDecisionApproveForSessionApprovalFactory : PermissionDecisionApproveForSessionApproval +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } +} + /// Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. /// The extension-permission-access variant of . [Experimental(Diagnostics.Experimental)] @@ -10713,6 +10836,7 @@ public partial class PermissionDecisionApproveForSession : PermissionDecision [JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalMemory), "memory")] [JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalCustomTool), "custom-tool")] [JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalFactory), "factory")] [JsonDerivedType(typeof(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess), "extension-permission-access")] public partial class PermissionDecisionApproveForLocationApproval { @@ -10827,6 +10951,21 @@ public partial class PermissionDecisionApproveForLocationApprovalExtensionManage public string? Operation { get; set; } } +/// Location-scoped factory approval, optionally narrowed by approval key. +/// The factory variant of . +[Experimental(Diagnostics.Experimental)] +public partial class PermissionDecisionApproveForLocationApprovalFactory : PermissionDecisionApproveForLocationApproval +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } +} + /// Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. /// The extension-permission-access variant of . [Experimental(Diagnostics.Experimental)] @@ -11140,7 +11279,7 @@ internal sealed class PermissionsSetAllowAllRequest [JsonPropertyName("mode")] public PermissionsAllowAllMode? Mode { get; set; } - /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. [JsonPropertyName("model")] public string? Model { get; set; } @@ -11476,6 +11615,7 @@ public sealed class PermissionsLocationsAddToolApprovalResult [JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMemory), "memory")] [JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCustomTool), "custom-tool")] [JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsFactory), "factory")] [JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess), "extension-permission-access")] public partial class PermissionsLocationsAddToolApprovalDetails { @@ -11590,6 +11730,21 @@ public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManageme public string? Operation { get; set; } } +/// Location-persisted factory approval, optionally narrowed by approval key. +/// The factory variant of . +[Experimental(Diagnostics.Experimental)] +public partial class PermissionsLocationsAddToolApprovalDetailsFactory : PermissionsLocationsAddToolApprovalDetails +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } +} + /// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. /// The extension-permission-access variant of . [Experimental(Diagnostics.Experimental)] @@ -13376,19 +13531,19 @@ internal sealed class SessionQueueProcessRequest [Experimental(Diagnostics.Experimental)] public sealed class EventsReadResult { - /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. + /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). [JsonPropertyName("cursor")] public string Cursor { get; set; } = string.Empty; - /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. + /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history. For a forward read the fallback starts from the beginning of the remaining history; for a backward read it falls back to the tail (the newest window). Because the fallback page is a fresh boundary snapshot rather than a continuation of the requested cursor, it may overlap events the consumer has already rendered — a backward fallback to the tail in particular can repeat the newest window. On 'expired', consumers should reset or rebase their local pagination state (or deduplicate by event id) before continuing from the returned cursor rather than blindly appending/prepending the fallback page. [JsonPropertyName("cursorStatus")] public EventsCursorStatus CursorStatus { get; set; } - /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. + /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. [JsonPropertyName("events")] public IList Events { get => field ??= []; set; } - /// True when the read returned `max` events and more events are available immediately. When false, the next read with a non-zero `waitMs` will block until a new event arrives or the wait expires. + /// True when more events are available in the read's direction. For a forward read, true means the batch returned `max` events and more are available immediately. For a backward read, true means older persisted events remain before the returned window. [JsonPropertyName("hasMore")] public bool HasMore { get; set; } } @@ -13397,6 +13552,10 @@ public sealed class EventsReadResult [Experimental(Diagnostics.Experimental)] internal sealed class EventLogReadRequest { + /// Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. + [JsonPropertyName("agentIds")] + public IList? AgentIds { get; set; } + /// Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. [JsonPropertyName("agentScope")] public EventsAgentScope? AgentScope { get; set; } @@ -13405,7 +13564,11 @@ internal sealed class EventLogReadRequest [JsonPropertyName("cursor")] public string? Cursor { get; set; } - /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). + /// Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. + [JsonPropertyName("direction")] + public EventsReadDirection? Direction { get; set; } + + /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. [JsonPropertyName("includeEphemeral")] public bool? IncludeEphemeral { get; set; } @@ -13421,7 +13584,7 @@ internal sealed class EventLogReadRequest [JsonPropertyName("types")] public JsonElement? Types { get; set; } - /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). + /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonPropertyName("waitMs")] public TimeSpan? Wait { get; set; } @@ -15277,6 +15440,135 @@ public override void Write(Utf8JsonWriter writer, DiscoveredMcpServerType value, } +/// Persisted extension discovery source. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct DiscoveredExtensionSource : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public DiscoveredExtensionSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Extension discovered from the user's extensions directory. + public static DiscoveredExtensionSource User { get; } = new("user"); + + /// Extension contributed by an installed plugin. + public static DiscoveredExtensionSource Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is DiscoveredExtensionSource other && Equals(other); + + /// + public bool Equals(DiscoveredExtensionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override DiscoveredExtensionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, DiscoveredExtensionSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionSource)); + } + } +} + + +/// Effective extension loading and agent-management mode. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct DiscoveredExtensionMode : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public DiscoveredExtensionMode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Extensions are not loaded. + public static DiscoveredExtensionMode Disabled { get; } = new("disabled"); + + /// Extensions are loaded, but the agent cannot create, reload, or manage them. + public static DiscoveredExtensionMode LoadOnly { get; } = new("load_only"); + + /// Extensions are loaded and the agent can create, reload, and manage them. + public static DiscoveredExtensionMode LoadAndAugment { get; } = new("load_and_augment"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is DiscoveredExtensionMode other && Equals(other); + + /// + public bool Equals(DiscoveredExtensionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override DiscoveredExtensionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, DiscoveredExtensionMode value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionMode)); + } + } +} + + /// Which tier this directory belongs to. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -21780,7 +22072,7 @@ public override void Write(Utf8JsonWriter writer, QueuePendingItemsKind value, J } -/// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. +/// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history (the beginning for a forward read, the tail for a backward read). The fallback page is a fresh boundary snapshot, not a continuation of the requested cursor, so it may overlap already-rendered events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate by event id) before continuing from the returned cursor. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -21906,6 +22198,69 @@ public override void Write(Utf8JsonWriter writer, EventsAgentScope value, JsonSe } +/// Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct EventsReadDirection : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public EventsReadDirection(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Page from the cursor toward newer events (default). + public static EventsReadDirection Forward { get; } = new("forward"); + + /// Tail-first: return the newest events and page toward older events. + public static EventsReadDirection Backward { get; } = new("backward"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(EventsReadDirection left, EventsReadDirection right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(EventsReadDirection left, EventsReadDirection right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is EventsReadDirection other && Equals(other); + + /// + public bool Equals(EventsReadDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override EventsReadDirection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, EventsReadDirection value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EventsReadDirection)); + } + } +} + + /// Client population used for the prediction baseline. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -22680,6 +23035,12 @@ internal async Task ConnectAsync(string? token = null, bool? enab Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// Extensions APIs. + public ServerExtensionsApi Extensions => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// Plugins APIs. public ServerPluginsApi Plugins => field ?? @@ -23003,6 +23364,48 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) } } +/// Provides server-scoped Extensions APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerExtensionsApi +{ + private readonly JsonRpc _rpc; + + internal ServerExtensionsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Discovers user and enabled installed-plugin extensions from persisted Copilot home state, including enablement preferences. Launch-scoped additional plugins are not included. + /// The to monitor for cancellation requests. The default is . + /// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + public async Task DiscoverAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "extensions.discover", [], cancellationToken); + } + + /// Persistently enables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.enable to update them. + /// Source-qualified user or plugin extension IDs to enable. + /// The to monitor for cancellation requests. The default is . + public async Task EnableAsync(IList ids, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(ids); + + var request = new DiscoveredExtensionsEnableRequest { Ids = ids }; + await CopilotClient.InvokeRpcAsync(_rpc, "extensions.enable", [request], cancellationToken); + } + + /// Persistently disables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.disable to update them. + /// Source-qualified user or plugin extension IDs to disable. + /// The to monitor for cancellation requests. The default is . + public async Task DisableAsync(IList ids, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(ids); + + var request = new DiscoveredExtensionsDisableRequest { Ids = ids }; + await CopilotClient.InvokeRpcAsync(_rpc, "extensions.disable", [request], cancellationToken); + } +} + /// Provides server-scoped Plugins APIs. [Experimental(Diagnostics.Experimental)] public sealed class ServerPluginsApi @@ -25152,6 +25555,20 @@ public async Task ListAsync(SessionAgentListRequest? request = null, return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.agent.list", [rpcRequest], cancellationToken); } + /// Sets an in-memory authored prompt override for an available agent. For built-in agents, this replaces only the static base prompt while preserving runtime-owned dynamic prompt composition and behavior. The special `general-purpose` agent is not overrideable. Overrides are not persisted; resumed and forked sessions start without them, so the host must re-apply them. + /// Stable effective agent id. Plugin namespace separators are normalized. + /// Replacement authored prompt. Empty text is valid. + /// The to monitor for cancellation requests. The default is . + public async Task SetPromptAsync(string id, string prompt, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(id); + ArgumentNullException.ThrowIfNull(prompt); + _session.ThrowIfDisposed(); + + var request = new AgentSetPromptRequest { SessionId = _session.SessionId, Id = id, Prompt = prompt }; + await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.agent.setPrompt", [request], cancellationToken); + } + /// Gets the currently selected custom agent for the session. /// The to monitor for cancellation requests. The default is . /// The currently selected custom agent, or null when using the default agent. @@ -25713,6 +26130,18 @@ public async Task HandlePendingRequestAsync(string return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.handlePendingRequest", [request], cancellationToken); } + /// Notifies the session that MCP OAuth authentication succeeded and updated credentials were persisted, so cached tool definitions can be refreshed. + /// Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. + /// Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. + /// The to monitor for cancellation requests. The default is . + public async Task AuthenticationStateChangedAsync(string? serverName = null, bool? refreshSessionToken = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new McpOauthAuthenticationStateChangedRequest { SessionId = _session.SessionId, ServerName = serverName, RefreshSessionToken = refreshSessionToken }; + await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.authenticationStateChanged", [request], cancellationToken); + } + /// Starts OAuth authentication for a remote MCP server. /// Name of the remote MCP server to authenticate. /// When true, clears any cached OAuth token for the server and runs a full new authorization. Use when the user explicitly wants to switch accounts or believes their session is stuck. @@ -26586,7 +27015,7 @@ public async Task SetApproveAllAsync(bool enable /// Sets the allow-all permission mode for the session. Used by attach-mode clients (e.g. LocalRpcSession's `/allow-all` forwarder) to flip the target session's permission state. The `on` mode swaps in unrestricted path and URL managers and emits `session.permissions_changed` on transition; the `auto` mode keeps normal prompt paths active while attaching LLM safety recommendations. The result returns the authoritative post-mutation state so callers can update their local mirrors without racing the `session.permissions_changed` notification on the same wire. /// Allow-all mode to apply. `on` enables full allow-all; `auto` enables advisory LLM auto-approval; `off` disables both. /// Legacy full allow-all toggle. Prefer `mode`; when `mode` is omitted, `enabled: true` is treated as `mode: "on"` and any other value is treated as `mode: "off"`. - /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. /// The to monitor for cancellation requests. The default is . /// Indicates whether the operation succeeded and reports the post-mutation state. @@ -27476,20 +27905,22 @@ internal EventLogApi(CopilotSession session) _session = session; } - /// Reads a batch of session events from a cursor, optionally waiting for new events. + /// Reads a batch of session events from a cursor, optionally waiting for new events. Supports tail-first reads via `direction: backward`. /// Opaque cursor returned by a previous read. Omit on the first call to start from the beginning of the session's persisted history. /// Maximum number of events to return in this batch (1–1000, default 200). - /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). + /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. /// Either '*' to receive all event types, or a non-empty list of event types to receive. /// Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. - /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). + /// Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. + /// Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. + /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. /// The to monitor for cancellation requests. The default is . /// Batch of session events returned by a read, with cursor and continuation metadata. - public async Task ReadAsync(string? cursor = null, long? max = null, TimeSpan? waitMs = null, object? types = null, EventsAgentScope? agentScope = null, bool? includeEphemeral = null, CancellationToken cancellationToken = default) + public async Task ReadAsync(string? cursor = null, long? max = null, TimeSpan? waitMs = null, object? types = null, EventsAgentScope? agentScope = null, IList? agentIds = null, EventsReadDirection? direction = null, bool? includeEphemeral = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new EventLogReadRequest { SessionId = _session.SessionId, Cursor = cursor, Max = max, Wait = waitMs, Types = CopilotClient.ToJsonElementForWire(types), AgentScope = agentScope, IncludeEphemeral = includeEphemeral }; + var request = new EventLogReadRequest { SessionId = _session.SessionId, Cursor = cursor, Max = max, Wait = waitMs, Types = CopilotClient.ToJsonElementForWire(types), AgentScope = agentScope, AgentIds = agentIds, Direction = direction, IncludeEphemeral = includeEphemeral }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.eventLog.read", [request], cancellationToken); } @@ -28251,8 +28682,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ExternalToolCompletedEvent), TypeInfoPropertyName = "SessionEventsExternalToolCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.ExternalToolRequestedData), TypeInfoPropertyName = "SessionEventsExternalToolRequestedData")] [JsonSerializable(typeof(GitHub.Copilot.ExternalToolRequestedEvent), TypeInfoPropertyName = "SessionEventsExternalToolRequestedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.FactoryPermissionOperation), TypeInfoPropertyName = "SessionEventsFactoryPermissionOperation")] +[JsonSerializable(typeof(GitHub.Copilot.FactoryPermissionPhase), TypeInfoPropertyName = "SessionEventsFactoryPermissionPhase")] [JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedData), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedData")] [JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedEvent), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.GitHubMcpToolConfig), TypeInfoPropertyName = "SessionEventsGitHubMcpToolConfig")] [JsonSerializable(typeof(GitHub.Copilot.GitHubRepoRef), TypeInfoPropertyName = "SessionEventsGitHubRepoRef")] [JsonSerializable(typeof(GitHub.Copilot.HandoffRepository), TypeInfoPropertyName = "SessionEventsHandoffRepository")] [JsonSerializable(typeof(GitHub.Copilot.HandoffSourceType), TypeInfoPropertyName = "SessionEventsHandoffSourceType")] @@ -28317,6 +28751,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestCustomTool), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestCustomTool")] [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestExtensionManagement), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestExtensionManagement")] [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestExtensionPermissionAccess), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestExtensionPermissionAccess")] +[JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestFactory), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestFactory")] [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestHook), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestHook")] [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestMcp), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestMcp")] [JsonSerializable(typeof(GitHub.Copilot.PermissionPromptRequestMemory), TypeInfoPropertyName = "SessionEventsPermissionPromptRequestMemory")] @@ -28329,6 +28764,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestCustomTool), TypeInfoPropertyName = "SessionEventsPermissionRequestCustomTool")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestExtensionManagement), TypeInfoPropertyName = "SessionEventsPermissionRequestExtensionManagement")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestExtensionPermissionAccess), TypeInfoPropertyName = "SessionEventsPermissionRequestExtensionPermissionAccess")] +[JsonSerializable(typeof(GitHub.Copilot.PermissionRequestFactory), TypeInfoPropertyName = "SessionEventsPermissionRequestFactory")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestHook), TypeInfoPropertyName = "SessionEventsPermissionRequestHook")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestMcp), TypeInfoPropertyName = "SessionEventsPermissionRequestMcp")] [JsonSerializable(typeof(GitHub.Copilot.PermissionRequestMemory), TypeInfoPropertyName = "SessionEventsPermissionRequestMemory")] @@ -28396,6 +28832,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationAgentIdle), TypeInfoPropertyName = "SessionEventsSystemNotificationAgentIdle")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationData), TypeInfoPropertyName = "SessionEventsSystemNotificationData")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationEvent), TypeInfoPropertyName = "SessionEventsSystemNotificationEvent")] +[JsonSerializable(typeof(GitHub.Copilot.SystemNotificationFactoryCompleted), TypeInfoPropertyName = "SessionEventsSystemNotificationFactoryCompleted")] +[JsonSerializable(typeof(GitHub.Copilot.SystemNotificationFactoryCompletedStatus), TypeInfoPropertyName = "SessionEventsSystemNotificationFactoryCompletedStatus")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationInstructionDiscovered), TypeInfoPropertyName = "SessionEventsSystemNotificationInstructionDiscovered")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationNewInboxMessage), TypeInfoPropertyName = "SessionEventsSystemNotificationNewInboxMessage")] [JsonSerializable(typeof(GitHub.Copilot.SystemNotificationShellCompleted), TypeInfoPropertyName = "SessionEventsSystemNotificationShellCompleted")] @@ -28457,6 +28895,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalCustomTool), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalCustomTool")] [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalExtensionManagement), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalExtensionManagement")] [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalExtensionPermissionAccess), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalExtensionPermissionAccess")] +[JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalFactory), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalFactory")] [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalMcp), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalMcp")] [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalMemory), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalMemory")] [JsonSerializable(typeof(GitHub.Copilot.UserToolSessionApprovalRead), TypeInfoPropertyName = "SessionEventsUserToolSessionApprovalRead")] @@ -28488,6 +28927,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(AgentReloadResult))] [JsonSerializable(typeof(AgentSelectRequest))] [JsonSerializable(typeof(AgentSelectResult))] +[JsonSerializable(typeof(AgentSetPromptRequest))] [JsonSerializable(typeof(AgentsDiscoverRequest))] [JsonSerializable(typeof(AgentsGetDiscoveryPathsRequest))] [JsonSerializable(typeof(AllowAllPermissionSetResult))] @@ -28547,6 +28987,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(DebugCollectLogsResult))] [JsonSerializable(typeof(DebugCollectLogsSkippedEntry))] [JsonSerializable(typeof(DiscoveredCanvas))] +[JsonSerializable(typeof(DiscoveredExtension))] +[JsonSerializable(typeof(DiscoveredExtensionPlugin))] +[JsonSerializable(typeof(DiscoveredExtensions))] +[JsonSerializable(typeof(DiscoveredExtensionsDisableRequest))] +[JsonSerializable(typeof(DiscoveredExtensionsEnableRequest))] [JsonSerializable(typeof(DiscoveredMcpServer))] [JsonSerializable(typeof(EnqueueCommandParams))] [JsonSerializable(typeof(EnqueueCommandResult))] @@ -28693,6 +29138,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpIsServerRunningResult))] [JsonSerializable(typeof(McpListToolsRequest))] [JsonSerializable(typeof(McpListToolsResult))] +[JsonSerializable(typeof(McpOauthAuthenticationStateChangedRequest))] [JsonSerializable(typeof(McpOauthHandlePendingRequest))] [JsonSerializable(typeof(McpOauthHandlePendingResult))] [JsonSerializable(typeof(McpOauthLoginRequest))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 6266daeec..0f3b53f48 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -1700,6 +1700,11 @@ public sealed partial class SessionStartData [JsonPropertyName("detachedFromSpawningParentSessionId")] public string? DetachedFromSpawningParentSessionId { get; set; } + /// Per-session GitHub MCP override persisted for cold resume. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("githubMcpToolConfig")] + public GitHubMcpToolConfig? GitHubMcpToolConfig { get; set; } + /// Identifier of the software producing the events (e.g., "copilot-agent"). [JsonPropertyName("producer")] public required string Producer { get; set; } @@ -2770,6 +2775,16 @@ public sealed partial class AssistantMessageData [JsonPropertyName("apiCallId")] public string? ApiCallId { get; set; } + /// Total messages the model call's response was split into, one per reasoning boundary. Absent for a single-message response; the last chunk is the one where chunkIndex is chunkCount - 1. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("chunkCount")] + public long? ChunkCount { get; set; } + + /// Zero-based position of this message within its model call's response. Absent when the response was not split into chunks. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("chunkIndex")] + public long? ChunkIndex { get; set; } + /// Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. [Experimental(Diagnostics.Experimental)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -6884,6 +6899,60 @@ public sealed partial class SystemNotificationInstructionDiscovered : SystemNoti public required string TriggerTool { get; set; } } +/// System notification metadata for a factory execution attempt that reached a terminal state. +/// The factory_completed variant of . +public sealed partial class SystemNotificationFactoryCompleted : SystemNotification +{ + /// + [JsonIgnore] + public override string Type => "factory_completed"; + + /// Execution attempt that reached this terminal state. + [JsonPropertyName("attempt")] + public required long Attempt { get; set; } + + /// Consumed AI usage in nano-AIU. + [JsonPropertyName("consumedNanoAiu")] + public required long ConsumedNanoAiu { get; set; } + + /// Subagents consumed by the run across all attempts. + [JsonPropertyName("consumedSubagents")] + public required long ConsumedSubagents { get; set; } + + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("elapsedMs")] + public required long ElapsedMs { get; set; } + + /// Persisted factory name. + [JsonPropertyName("factoryName")] + public required string FactoryName { get; set; } + + /// Machine-readable terminal failure details, when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("failure")] + public JsonElement? Failure { get; set; } + + /// Bounded prompt-safe preview of the completed result. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(256)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resultPreview")] + public string? ResultPreview { get; set; } + + /// Actionable run_factory resume guidance for a resource-limit failure. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("retryGuidance")] + public string? RetryGuidance { get; set; } + + /// Factory run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } + + /// Terminal status reached by this execution attempt. + [JsonPropertyName("status")] + public required SystemNotificationFactoryCompletedStatus Status { get; set; } +} + /// System notification metadata from an external host that does not match a runtime-owned notification kind. /// The unclassified variant of . public sealed partial class SystemNotificationUnclassified : SystemNotification @@ -6909,6 +6978,7 @@ public sealed partial class SystemNotificationUnclassified : SystemNotification [JsonDerivedType(typeof(SystemNotificationShellCompleted), "shell_completed")] [JsonDerivedType(typeof(SystemNotificationShellDetachedCompleted), "shell_detached_completed")] [JsonDerivedType(typeof(SystemNotificationInstructionDiscovered), "instruction_discovered")] +[JsonDerivedType(typeof(SystemNotificationFactoryCompleted), "factory_completed")] [JsonDerivedType(typeof(SystemNotificationUnclassified), "unclassified")] public partial class SystemNotification { @@ -7321,6 +7391,98 @@ public sealed partial class PermissionRequestExtensionManagement : PermissionReq public string? ToolCallId { get; set; } } +/// A declared phase shown in a factory permission prompt. +/// Nested data type for FactoryPermissionPhase. +public sealed partial class FactoryPermissionPhase +{ + /// Optional phase detail. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("detail")] + public string? Detail { get; set; } + + /// Phase title. + [JsonPropertyName("title")] + public required string Title { get; set; } +} + +/// Factory run or authoring permission request. +/// The factory variant of . +public sealed partial class PermissionRequestFactory : PermissionRequest +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Canonical key used for scoped factory approvals. + [JsonPropertyName("approvalKey")] + public required string ApprovalKey { get; set; } + + /// Whether this factory is eligible for persistent approval. + [JsonPropertyName("canPersistApproval")] + public required bool CanPersistApproval { get; set; } + + /// Gets or sets the declaredMaxAiCredits value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxAiCredits")] + public double? DeclaredMaxAiCredits { get; set; } + + /// Gets or sets the declaredMaxConcurrentSubagents value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxConcurrentSubagents")] + public long? DeclaredMaxConcurrentSubagents { get; set; } + + /// Gets or sets the declaredMaxTotalSubagents value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxTotalSubagents")] + public long? DeclaredMaxTotalSubagents { get; set; } + + /// Gets or sets the declaredTimeoutSeconds value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredTimeoutSeconds")] + public double? DeclaredTimeoutSeconds { get; set; } + + /// Factory description. + [JsonPropertyName("description")] + public required string Description { get; set; } + + /// Effective AI-credit limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } + + /// Effective concurrent-subagent limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } + + /// Effective total-subagent limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } + + /// Factory name. + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// Factory operation, either run or author. + [JsonPropertyName("operation")] + public required FactoryPermissionOperation Operation { get; set; } + + /// Declared factory phases. + [JsonPropertyName("phases")] + public required FactoryPermissionPhase[] Phases { get; set; } + + /// Effective active-time limit in seconds; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } + + /// Tool call ID that triggered this permission request. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } +} + /// Extension permission access request. /// The extension-permission-access variant of . public sealed partial class PermissionRequestExtensionPermissionAccess : PermissionRequest @@ -7357,6 +7519,7 @@ public sealed partial class PermissionRequestExtensionPermissionAccess : Permiss [JsonDerivedType(typeof(PermissionRequestCustomTool), "custom-tool")] [JsonDerivedType(typeof(PermissionRequestHook), "hook")] [JsonDerivedType(typeof(PermissionRequestExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionRequestFactory), "factory")] [JsonDerivedType(typeof(PermissionRequestExtensionPermissionAccess), "extension-permission-access")] public partial class PermissionRequest { @@ -7779,6 +7942,95 @@ public sealed partial class PermissionPromptRequestExtensionManagement : Permiss public string? ToolCallId { get; set; } } +/// Factory run or authoring permission prompt. +/// The factory variant of . +public sealed partial class PermissionPromptRequestFactory : PermissionPromptRequest +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Canonical key used for scoped factory approvals. + [JsonPropertyName("approvalKey")] + public required string ApprovalKey { get; set; } + + /// Auto-approval judge information for this request; present only when auto mode is enabled. + [Experimental(Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoApproval")] + public PermissionAutoApproval? AutoApproval { get; set; } + + /// Whether this factory is eligible for persistent approval. + [JsonPropertyName("canPersistApproval")] + public required bool CanPersistApproval { get; set; } + + /// Gets or sets the declaredMaxAiCredits value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxAiCredits")] + public double? DeclaredMaxAiCredits { get; set; } + + /// Gets or sets the declaredMaxConcurrentSubagents value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxConcurrentSubagents")] + public long? DeclaredMaxConcurrentSubagents { get; set; } + + /// Gets or sets the declaredMaxTotalSubagents value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredMaxTotalSubagents")] + public long? DeclaredMaxTotalSubagents { get; set; } + + /// Gets or sets the declaredTimeoutSeconds value. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("declaredTimeoutSeconds")] + public double? DeclaredTimeoutSeconds { get; set; } + + /// Factory description. + [JsonPropertyName("description")] + public required string Description { get; set; } + + /// Whether managed policy requires a human response and forbids host auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("managedApprovalRequired")] + public bool? ManagedApprovalRequired { get; set; } + + /// Effective AI-credit limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } + + /// Effective concurrent-subagent limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } + + /// Effective total-subagent limit; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } + + /// Factory name. + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// Factory operation, either run or author. + [JsonPropertyName("operation")] + public required FactoryPermissionOperation Operation { get; set; } + + /// Declared factory phases. + [JsonPropertyName("phases")] + public required FactoryPermissionPhase[] Phases { get; set; } + + /// Effective active-time limit in seconds; omitted means unlimited. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } + + /// Tool call ID that triggered this permission request. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } +} + /// Extension permission access prompt. /// The extension-permission-access variant of . public sealed partial class PermissionPromptRequestExtensionPermissionAccess : PermissionPromptRequest @@ -7822,6 +8074,7 @@ public sealed partial class PermissionPromptRequestExtensionPermissionAccess : P [JsonDerivedType(typeof(PermissionPromptRequestPath), "path")] [JsonDerivedType(typeof(PermissionPromptRequestHook), "hook")] [JsonDerivedType(typeof(PermissionPromptRequestExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionPromptRequestFactory), "factory")] [JsonDerivedType(typeof(PermissionPromptRequestExtensionPermissionAccess), "extension-permission-access")] public partial class PermissionPromptRequest { @@ -7924,6 +8177,20 @@ public sealed partial class UserToolSessionApprovalExtensionManagement : UserToo public string? Operation { get; set; } } +/// Session-scoped factory approval, optionally narrowed by approval key. +/// The factory variant of . +public sealed partial class UserToolSessionApprovalFactory : UserToolSessionApproval +{ + /// + [JsonIgnore] + public override string Kind => "factory"; + + /// Optional factory operation name or canonical approval key. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } +} + /// Session-scoped tool-approval rule for an extension's permission-gated capability access, keyed by extension name. /// The extension-permission-access variant of . public sealed partial class UserToolSessionApprovalExtensionPermissionAccess : UserToolSessionApproval @@ -7949,6 +8216,7 @@ public sealed partial class UserToolSessionApprovalExtensionPermissionAccess : U [JsonDerivedType(typeof(UserToolSessionApprovalMemory), "memory")] [JsonDerivedType(typeof(UserToolSessionApprovalCustomTool), "custom-tool")] [JsonDerivedType(typeof(UserToolSessionApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(UserToolSessionApprovalFactory), "factory")] [JsonDerivedType(typeof(UserToolSessionApprovalExtensionPermissionAccess), "extension-permission-access")] public partial class UserToolSessionApproval { @@ -10818,6 +11086,73 @@ public override void Write(Utf8JsonWriter writer, SystemNotificationAgentComplet } } +/// Terminal status reached by a factory execution attempt. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SystemNotificationFactoryCompletedStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SystemNotificationFactoryCompletedStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The factory completed successfully. + public static SystemNotificationFactoryCompletedStatus Completed { get; } = new("completed"); + + /// The factory was halted. + public static SystemNotificationFactoryCompletedStatus Halted { get; } = new("halted"); + + /// The factory was cancelled. + public static SystemNotificationFactoryCompletedStatus Cancelled { get; } = new("cancelled"); + + /// The factory failed. + public static SystemNotificationFactoryCompletedStatus Error { get; } = new("error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SystemNotificationFactoryCompletedStatus left, SystemNotificationFactoryCompletedStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SystemNotificationFactoryCompletedStatus left, SystemNotificationFactoryCompletedStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SystemNotificationFactoryCompletedStatus other && Equals(other); + + /// + public bool Equals(SystemNotificationFactoryCompletedStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override SystemNotificationFactoryCompletedStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SystemNotificationFactoryCompletedStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SystemNotificationFactoryCompletedStatus)); + } + } +} + /// Whether this is a store or vote memory operation. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -10940,6 +11275,67 @@ public override void Write(Utf8JsonWriter writer, PermissionRequestMemoryDirecti } } +/// Operation gated by a factory permission request. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct FactoryPermissionOperation : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public FactoryPermissionOperation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Running a registered factory, which spends subagents, active time, and AI credits under the approved limits. + public static FactoryPermissionOperation Run { get; } = new("run"); + + /// Authoring a factory, which writes JavaScript into a session-scoped extension and loads it. + public static FactoryPermissionOperation Author { get; } = new("author"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(FactoryPermissionOperation left, FactoryPermissionOperation right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(FactoryPermissionOperation left, FactoryPermissionOperation right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is FactoryPermissionOperation other && Equals(other); + + /// + public bool Equals(FactoryPermissionOperation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override FactoryPermissionOperation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, FactoryPermissionOperation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FactoryPermissionOperation)); + } + } +} + /// Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -12508,8 +12904,10 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ExternalToolCompletedEvent))] [JsonSerializable(typeof(ExternalToolRequestedData))] [JsonSerializable(typeof(ExternalToolRequestedEvent))] +[JsonSerializable(typeof(FactoryPermissionPhase))] [JsonSerializable(typeof(FactoryRunUpdatedData))] [JsonSerializable(typeof(FactoryRunUpdatedEvent))] +[JsonSerializable(typeof(GitHubMcpToolConfig))] [JsonSerializable(typeof(GitHubRepoRef))] [JsonSerializable(typeof(HandoffRepository))] [JsonSerializable(typeof(HeaderEntry))] @@ -12559,6 +12957,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PermissionPromptRequestCustomTool))] [JsonSerializable(typeof(PermissionPromptRequestExtensionManagement))] [JsonSerializable(typeof(PermissionPromptRequestExtensionPermissionAccess))] +[JsonSerializable(typeof(PermissionPromptRequestFactory))] [JsonSerializable(typeof(PermissionPromptRequestHook))] [JsonSerializable(typeof(PermissionPromptRequestMcp))] [JsonSerializable(typeof(PermissionPromptRequestMemory))] @@ -12570,6 +12969,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PermissionRequestCustomTool))] [JsonSerializable(typeof(PermissionRequestExtensionManagement))] [JsonSerializable(typeof(PermissionRequestExtensionPermissionAccess))] +[JsonSerializable(typeof(PermissionRequestFactory))] [JsonSerializable(typeof(PermissionRequestHook))] [JsonSerializable(typeof(PermissionRequestMcp))] [JsonSerializable(typeof(PermissionRequestMemory))] @@ -12729,6 +13129,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SystemNotificationAgentIdle))] [JsonSerializable(typeof(SystemNotificationData))] [JsonSerializable(typeof(SystemNotificationEvent))] +[JsonSerializable(typeof(SystemNotificationFactoryCompleted))] [JsonSerializable(typeof(SystemNotificationInstructionDiscovered))] [JsonSerializable(typeof(SystemNotificationNewInboxMessage))] [JsonSerializable(typeof(SystemNotificationShellCompleted))] @@ -12786,6 +13187,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(UserToolSessionApprovalCustomTool))] [JsonSerializable(typeof(UserToolSessionApprovalExtensionManagement))] [JsonSerializable(typeof(UserToolSessionApprovalExtensionPermissionAccess))] +[JsonSerializable(typeof(UserToolSessionApprovalFactory))] [JsonSerializable(typeof(UserToolSessionApprovalMcp))] [JsonSerializable(typeof(UserToolSessionApprovalMemory))] [JsonSerializable(typeof(UserToolSessionApprovalRead))] diff --git a/go/rpc/permission_request_managed_approval.go b/go/rpc/permission_request_managed_approval.go index 816181f20..020626893 100644 --- a/go/rpc/permission_request_managed_approval.go +++ b/go/rpc/permission_request_managed_approval.go @@ -26,6 +26,12 @@ func (r PermissionRequestExtensionPermissionAccess) RequiresManagedApproval() bo return managedApprovalRequired(r.ManagedApprovalRequired) } +// RequiresManagedApproval reports whether managed policy requires an explicit +// human decision for this request. +func (r PermissionRequestFactory) RequiresManagedApproval() bool { + return managedApprovalRequired(r.ManagedApprovalRequired) +} + // RequiresManagedApproval reports whether managed policy requires an explicit // human decision for this request. func (r PermissionRequestHook) RequiresManagedApproval() bool { diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 15b7c0cd6..a34dccd2e 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -433,6 +433,16 @@ type AgentSelectResult struct { Agent AgentInfo `json:"agent"` } +// An in-memory authored prompt override for an available agent. +// Experimental: AgentSetPromptRequest is part of an experimental API and may change or be +// removed. +type AgentSetPromptRequest struct { + // Stable effective agent id. Plugin namespace separators are normalized. + ID string `json:"id"` + // Replacement authored prompt. Empty text is valid. + Prompt string `json:"prompt"` +} + // Optional project paths to include when enumerating agent discovery directories. // Experimental: AgentsGetDiscoveryPathsRequest is part of an experimental API and may // change or be removed. @@ -1860,6 +1870,59 @@ type DiscoveredCanvas struct { InputSchema any `json:"inputSchema,omitempty"` } +// Discovered extension metadata and persistent enablement state. +// Experimental: DiscoveredExtension is part of an experimental API and may change or be +// removed. +type DiscoveredExtension struct { + // Whether this extension's persistent per-ID preference is enabled + Enabled bool `json:"enabled"` + // Source-qualified ID accepted by both server and session extension enablement methods + ID string `json:"id"` + // Human-readable extension name + Name string `json:"name"` + // Absolute path to the extension entry module, suitable for revealing it in a file manager + Path string `json:"path"` + // Containing plugin metadata for plugin-contributed extensions + Plugin *DiscoveredExtensionPlugin `json:"plugin,omitempty"` + // Discovery source + Source DiscoveredExtensionSource `json:"source"` +} + +// Installed plugin that contributes a discovered extension. +// Experimental: DiscoveredExtensionPlugin is part of an experimental API and may change or +// be removed. +type DiscoveredExtensionPlugin struct { + // Installed plugin name + Name string `json:"name"` +} + +// Extensions discovered from persisted Copilot home state and their effective loading mode. +// Launch-scoped additional plugins are not included. +// Experimental: DiscoveredExtensions is part of an experimental API and may change or be +// removed. +type DiscoveredExtensions struct { + // Discovered user and enabled installed-plugin extensions from persisted Copilot home state + Extensions []DiscoveredExtension `json:"extensions"` + // Effective extension loading mode. Defaults to load_and_augment when unset. + Mode DiscoveredExtensionMode `json:"mode"` +} + +// Source-qualified extension identifiers to persistently disable for future sessions. +// Experimental: DiscoveredExtensionsDisableRequest is part of an experimental API and may +// change or be removed. +type DiscoveredExtensionsDisableRequest struct { + // Source-qualified user or plugin extension IDs to disable + IDs []string `json:"ids"` +} + +// Source-qualified extension identifiers to persistently enable for future sessions. +// Experimental: DiscoveredExtensionsEnableRequest is part of an experimental API and may +// change or be removed. +type DiscoveredExtensionsEnableRequest struct { + // Source-qualified user or plugin extension IDs to enable + IDs []string `json:"ids"` +} + // MCP server discovered by `mcp.discover`, with config source, optional plugin source, // transport type, and enabled state. // Experimental: DiscoveredMCPServer is part of an experimental API and may change or be @@ -1901,6 +1964,11 @@ type EnqueueCommandResult struct { // Experimental: EventLogReadRequest is part of an experimental API and may change or be // removed. type EventLogReadRequest struct { + // Optional non-empty list of subagent identifiers. When provided, only events owned by one + // of these agents are returned; ownership recognizes the event envelope's agentId plus + // legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over + // agentScope. + AgentIDs []string `json:"agentIds,omitzero"` // Agent-scope filter: 'primary' returns only main-agent events plus events whose type // starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns // events from all agents (matching wildcard-subscription behavior). Default is 'all' to @@ -1909,10 +1977,23 @@ type EventLogReadRequest struct { // Opaque cursor returned by a previous read. Omit on the first call to start from the // beginning of the session's persisted history. Cursor *string `json:"cursor,omitempty"` + // Direction to page through the session's persisted event history. 'forward' (default) + // pages from the cursor toward newer events (or from the start of history when no cursor is + // given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` + // events, and the returned cursor pages toward OLDER events on subsequent backward reads. + // Events within a returned batch are always in chronological (oldest-to-newest) order, even + // for a backward read. Backward reads cover PERSISTED history only; ephemeral events are + // never returned by a backward read. `direction` selects the INITIAL read only: the + // returned cursor is self-describing, so a continuation read pages in the cursor's own + // direction regardless of the `direction` passed alongside it — a forward cursor always + // pages forward and a backward cursor always pages backward. Pass the direction that + // matches the cursor to avoid confusion. + Direction *EventsReadDirection `json:"direction,omitempty"` // When false, skip ephemeral events entirely and return only durable (persisted) events. // History-backfill callers that discard ephemerals anyway should set this so the read is // bounded by the durable log length instead of racing the ephemeral ring on a busy session. // Defaults to true (ephemerals are interleaved with durable events in creation order). + // Ignored by backward reads, which always cover persisted history only. IncludeEphemeral *bool `json:"includeEphemeral,omitempty"` // Maximum number of events to return in this batch (1–1000, default 200). Max *int64 `json:"max,omitempty"` @@ -1922,7 +2003,10 @@ type EventLogReadRequest struct { // (default) returns immediately even if no events are available. Capped at 30000ms. // Ephemeral events that arrive during the wait are delivered in this batch but are NOT // replayable on a subsequent read (use a non-zero waitMs in your next call to capture - // future ephemerals as they happen). + // future ephemerals as they happen). This applies to forward reads only: a backward read + // always returns immediately and ignores `waitMs`, because backward paging covers persisted + // history only while new events append at the tail (the opposite end from a backward page), + // so no blocking or ephemeral delivery can occur. WaitMs *int32 `json:"waitMs,omitempty"` } @@ -1960,21 +2044,31 @@ type EventLogTypes struct { // removed. type EventsReadResult struct { // Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue - // from where this read left off. Always present, even when no events were returned. + // from where this read left off. Always present, even when no events were returned. For a + // backward read this cursor pages toward OLDER events; keep passing `direction: backward` + // with it (the cursor is also self-describing, so backward paging continues correctly). Cursor string `json:"cursor"` // Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor // referred to an event that no longer exists in history (e.g. truncated or compacted away) - // and the read started from the beginning of the remaining history. + // and the read fell back to a boundary of the remaining history. For a forward read the + // fallback starts from the beginning of the remaining history; for a backward read it falls + // back to the tail (the newest window). Because the fallback page is a fresh boundary + // snapshot rather than a continuation of the requested cursor, it may overlap events the + // consumer has already rendered — a backward fallback to the tail in particular can repeat + // the newest window. On 'expired', consumers should reset or rebase their local pagination + // state (or deduplicate by event id) before continuing from the returned cursor rather than + // blindly appending/prepending the fallback page. CursorStatus EventsCursorStatus `json:"cursorStatus"` // Session events for this batch, merged into a single stream in creation order: durable // (persisted) events and ephemeral events interleave exactly as they were emitted. Set // `includeEphemeral: false` to receive only durable events. Ephemeral events are never // replayable once pruned from the in-memory ring, so a consumer that needs them should keep - // reading with a non-zero `waitMs`. + // reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window + // contains persisted events only, still in chronological (oldest-to-newest) append order. Events []SessionEvent `json:"events"` - // True when the read returned `max` events and more events are available immediately. When - // false, the next read with a non-zero `waitMs` will block until a new event arrives or the - // wait expires. + // True when more events are available in the read's direction. For a forward read, true + // means the batch returned `max` events and more are available immediately. For a backward + // read, true means older persisted events remain before the returned window. HasMore bool `json:"hasMore"` } @@ -2030,6 +2124,11 @@ type ExtensionsDisableRequest struct { ID string `json:"id"` } +// Experimental: ExtensionsDisableResult is part of an experimental API and may change or be +// removed. +type ExtensionsDisableResult struct { +} + // Source-qualified extension identifier to enable for the session. // Experimental: ExtensionsEnableRequest is part of an experimental API and may change or be // removed. @@ -2038,6 +2137,11 @@ type ExtensionsEnableRequest struct { ID string `json:"id"` } +// Experimental: ExtensionsEnableResult is part of an experimental API and may change or be +// removed. +type ExtensionsEnableResult struct { +} + // Tool call result (string or expanded result object) // Experimental: ExternalToolResult is part of an experimental API and may change or be // removed. @@ -4181,6 +4285,18 @@ type MCPListToolsResult struct { Tools []MCPTools `json:"tools"` } +// Identifies the MCP server whose persisted OAuth credentials were updated. +// Experimental: MCPOauthAuthenticationStateChangedRequest is part of an experimental API +// and may change or be removed. +type MCPOauthAuthenticationStateChangedRequest struct { + // Whether the target session must mint a session-scoped access token instead of reusing a + // shared access token persisted by another session. + RefreshSessionToken *bool `json:"refreshSessionToken,omitempty"` + // Name of the MCP server whose OAuth credentials were updated. Omit only when the host + // cannot identify the server. + ServerName *string `json:"serverName,omitempty"` +} + // Pending MCP OAuth request ID and host-provided token or cancellation response. // Experimental: MCPOauthHandlePendingRequest is part of an experimental API and may change // or be removed. @@ -5707,6 +5823,21 @@ func (PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess) Kin return PermissionDecisionApproveForLocationApprovalKindExtensionPermissionAccess } +// Location-scoped factory approval, optionally narrowed by approval key. +// Experimental: PermissionDecisionApproveForLocationApprovalFactory is part of an +// experimental API and may change or be removed. +type PermissionDecisionApproveForLocationApprovalFactory struct { + // Optional factory operation name or canonical approval key; when omitted, the approval + // covers all factory operations. + ApprovalKey *string `json:"approvalKey,omitempty"` +} + +func (PermissionDecisionApproveForLocationApprovalFactory) permissionDecisionApproveForLocationApproval() { +} +func (PermissionDecisionApproveForLocationApprovalFactory) Kind() PermissionDecisionApproveForLocationApprovalKind { + return PermissionDecisionApproveForLocationApprovalKindFactory +} + // Location-scoped approval details for an MCP server tool, or all tools on the server when // `toolName` is null. // Experimental: PermissionDecisionApproveForLocationApprovalMCP is part of an experimental @@ -5852,6 +5983,21 @@ func (PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess) Kind return PermissionDecisionApproveForSessionApprovalKindExtensionPermissionAccess } +// Session-scoped factory approval, optionally narrowed by approval key. +// Experimental: PermissionDecisionApproveForSessionApprovalFactory is part of an +// experimental API and may change or be removed. +type PermissionDecisionApproveForSessionApprovalFactory struct { + // Optional factory operation name or canonical approval key; when omitted, the approval + // covers all factory operations. + ApprovalKey *string `json:"approvalKey,omitempty"` +} + +func (PermissionDecisionApproveForSessionApprovalFactory) permissionDecisionApproveForSessionApproval() { +} +func (PermissionDecisionApproveForSessionApprovalFactory) Kind() PermissionDecisionApproveForSessionApprovalKind { + return PermissionDecisionApproveForSessionApprovalKindFactory +} + // Session-scoped approval details for an MCP server tool, or all tools on the server when // `toolName` is null. // Experimental: PermissionDecisionApproveForSessionApprovalMCP is part of an experimental @@ -6265,6 +6411,21 @@ func (PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess) Kind( return PermissionsLocationsAddToolApprovalDetailsKindExtensionPermissionAccess } +// Location-persisted factory approval, optionally narrowed by approval key. +// Experimental: PermissionsLocationsAddToolApprovalDetailsFactory is part of an +// experimental API and may change or be removed. +type PermissionsLocationsAddToolApprovalDetailsFactory struct { + // Optional factory operation name or canonical approval key; when omitted, the approval + // covers all factory operations. + ApprovalKey *string `json:"approvalKey,omitempty"` +} + +func (PermissionsLocationsAddToolApprovalDetailsFactory) permissionsLocationsAddToolApprovalDetails() { +} +func (PermissionsLocationsAddToolApprovalDetailsFactory) Kind() PermissionsLocationsAddToolApprovalDetailsKind { + return PermissionsLocationsAddToolApprovalDetailsKindFactory +} + // Location-persisted tool approval details for an MCP server tool, or all tools when // `toolName` is null. // Experimental: PermissionsLocationsAddToolApprovalDetailsMCP is part of an experimental @@ -6425,7 +6586,8 @@ type PermissionsSetAllowAllRequest struct { // auto-approval; `off` disables both. Mode *PermissionsAllowAllMode `json:"mode,omitempty"` // Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when - // `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + // `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge + // model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. Model *string `json:"model,omitempty"` // Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. Source *PermissionsSetAllowAllSource `json:"source,omitempty"` @@ -7941,6 +8103,13 @@ type RuntimeShutdownResult struct { type SandboxConfig struct { // Whether to auto-add the current working directory to readwritePaths. Default: true. AddCurrentWorkingDirectory *bool `json:"addCurrentWorkingDirectory,omitempty"` + // Whether to auto-grant read access to common developer-tool caches, registries, and + // toolchains in their default home locations (cargo, go, npm, Maven, and more), plus + // read-write access to (and, on Unix, up-front creation of) the scratch caches builds write + // on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so + // builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; + // set to false to opt out). + AllowDevToolCaches *bool `json:"allowDevToolCaches,omitempty"` // Whether sandboxing is enabled for the session. Enabled bool `json:"enabled"` // Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the @@ -8428,6 +8597,11 @@ type SessionAgentListRequest struct { IncludePrompt *bool `json:"includePrompt,omitempty"` } +// Experimental: SessionAgentSetPromptResult is part of an experimental API and may change +// or be removed. +type SessionAgentSetPromptResult struct { +} + // Authentication status and account metadata for the session. // Experimental: SessionAuthStatus is part of an experimental API and may change or be // removed. @@ -9237,6 +9411,11 @@ type SessionMCPDisableResult struct { type SessionMCPEnableResult struct { } +// Experimental: SessionMCPOauthAuthenticationStateChangedResult is part of an experimental +// API and may change or be removed. +type SessionMCPOauthAuthenticationStateChangedResult struct { +} + // Experimental: SessionMCPRegisterExternalClientResult is part of an experimental API and // may change or be removed. type SessionMCPRegisterExternalClientResult struct { @@ -12161,6 +12340,19 @@ func (UserToolSessionApprovalExtensionPermissionAccess) Kind() UserToolSessionAp return UserToolSessionApprovalKindExtensionPermissionAccess } +// Session-scoped factory approval, optionally narrowed by approval key. +// Experimental: UserToolSessionApprovalFactory is part of an experimental API and may +// change or be removed. +type UserToolSessionApprovalFactory struct { + // Optional factory operation name or canonical approval key + ApprovalKey *string `json:"approvalKey,omitempty"` +} + +func (UserToolSessionApprovalFactory) userToolSessionApproval() {} +func (UserToolSessionApprovalFactory) Kind() UserToolSessionApprovalKind { + return UserToolSessionApprovalKindFactory +} + // Session-scoped tool-approval rule for an MCP server tool, or all tools on the server when // `toolName` is null. // Experimental: UserToolSessionApprovalMCP is part of an experimental API and may change or @@ -12898,6 +13090,32 @@ const ( DebugCollectLogsSourceShellLog DebugCollectLogsSource = "shell-log" ) +// Effective extension loading and agent-management mode +// Experimental: DiscoveredExtensionMode is part of an experimental API and may change or be +// removed. +type DiscoveredExtensionMode string + +const ( + // Extensions are not loaded. + DiscoveredExtensionModeDisabled DiscoveredExtensionMode = "disabled" + // Extensions are loaded and the agent can create, reload, and manage them. + DiscoveredExtensionModeLoadAndAugment DiscoveredExtensionMode = "load_and_augment" + // Extensions are loaded, but the agent cannot create, reload, or manage them. + DiscoveredExtensionModeLoadOnly DiscoveredExtensionMode = "load_only" +) + +// Persisted extension discovery source +// Experimental: DiscoveredExtensionSource is part of an experimental API and may change or +// be removed. +type DiscoveredExtensionSource string + +const ( + // Extension contributed by an installed plugin. + DiscoveredExtensionSourcePlugin DiscoveredExtensionSource = "plugin" + // Extension discovered from the user's extensions directory. + DiscoveredExtensionSourceUser DiscoveredExtensionSource = "user" +) + // Server transport type: stdio, http, sse (deprecated), or memory // Experimental: DiscoveredMCPServerType is part of an experimental API and may change or be // removed. @@ -12937,7 +13155,11 @@ const ( // Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor // referred to an event that no longer exists in history (e.g. truncated or compacted away) -// and the read started from the beginning of the remaining history. +// and the read fell back to a boundary of the remaining history (the beginning for a +// forward read, the tail for a backward read). The fallback page is a fresh boundary +// snapshot, not a continuation of the requested cursor, so it may overlap already-rendered +// events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate +// by event id) before continuing from the returned cursor. // Experimental: EventsCursorStatus is part of an experimental API and may change or be // removed. type EventsCursorStatus string @@ -12949,6 +13171,21 @@ const ( EventsCursorStatusOk EventsCursorStatus = "ok" ) +// Direction to page through the session's persisted event history. 'forward' pages from the +// cursor toward newer events; 'backward' returns the newest window first (tail-first) and +// pages toward older events. Events within a returned batch are always chronological +// (oldest-to-newest), even for a backward read. +// Experimental: EventsReadDirection is part of an experimental API and may change or be +// removed. +type EventsReadDirection string + +const ( + // Tail-first: return the newest events and page toward older events. + EventsReadDirectionBackward EventsReadDirection = "backward" + // Page from the cursor toward newer events (default). + EventsReadDirectionForward EventsReadDirection = "forward" +) + // Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin // (installed plugin), or session (session-state//extensions/) // Experimental: ExtensionSource is part of an experimental API and may change or be removed. @@ -13796,6 +14033,7 @@ const ( PermissionDecisionApproveForLocationApprovalKindCustomTool PermissionDecisionApproveForLocationApprovalKind = "custom-tool" PermissionDecisionApproveForLocationApprovalKindExtensionManagement PermissionDecisionApproveForLocationApprovalKind = "extension-management" PermissionDecisionApproveForLocationApprovalKindExtensionPermissionAccess PermissionDecisionApproveForLocationApprovalKind = "extension-permission-access" + PermissionDecisionApproveForLocationApprovalKindFactory PermissionDecisionApproveForLocationApprovalKind = "factory" PermissionDecisionApproveForLocationApprovalKindMCP PermissionDecisionApproveForLocationApprovalKind = "mcp" PermissionDecisionApproveForLocationApprovalKindMCPSampling PermissionDecisionApproveForLocationApprovalKind = "mcp-sampling" PermissionDecisionApproveForLocationApprovalKindMemory PermissionDecisionApproveForLocationApprovalKind = "memory" @@ -13811,6 +14049,7 @@ const ( PermissionDecisionApproveForSessionApprovalKindCustomTool PermissionDecisionApproveForSessionApprovalKind = "custom-tool" PermissionDecisionApproveForSessionApprovalKindExtensionManagement PermissionDecisionApproveForSessionApprovalKind = "extension-management" PermissionDecisionApproveForSessionApprovalKindExtensionPermissionAccess PermissionDecisionApproveForSessionApprovalKind = "extension-permission-access" + PermissionDecisionApproveForSessionApprovalKindFactory PermissionDecisionApproveForSessionApprovalKind = "factory" PermissionDecisionApproveForSessionApprovalKindMCP PermissionDecisionApproveForSessionApprovalKind = "mcp" PermissionDecisionApproveForSessionApprovalKindMCPSampling PermissionDecisionApproveForSessionApprovalKind = "mcp-sampling" PermissionDecisionApproveForSessionApprovalKindMemory PermissionDecisionApproveForSessionApprovalKind = "memory" @@ -13887,6 +14126,7 @@ const ( PermissionsLocationsAddToolApprovalDetailsKindCustomTool PermissionsLocationsAddToolApprovalDetailsKind = "custom-tool" PermissionsLocationsAddToolApprovalDetailsKindExtensionManagement PermissionsLocationsAddToolApprovalDetailsKind = "extension-management" PermissionsLocationsAddToolApprovalDetailsKindExtensionPermissionAccess PermissionsLocationsAddToolApprovalDetailsKind = "extension-permission-access" + PermissionsLocationsAddToolApprovalDetailsKindFactory PermissionsLocationsAddToolApprovalDetailsKind = "factory" PermissionsLocationsAddToolApprovalDetailsKindMCP PermissionsLocationsAddToolApprovalDetailsKind = "mcp" PermissionsLocationsAddToolApprovalDetailsKindMCPSampling PermissionsLocationsAddToolApprovalDetailsKind = "mcp-sampling" PermissionsLocationsAddToolApprovalDetailsKindMemory PermissionsLocationsAddToolApprovalDetailsKind = "memory" @@ -14891,6 +15131,7 @@ const ( UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKind = "custom-tool" UserToolSessionApprovalKindExtensionManagement UserToolSessionApprovalKind = "extension-management" UserToolSessionApprovalKindExtensionPermissionAccess UserToolSessionApprovalKind = "extension-permission-access" + UserToolSessionApprovalKindFactory UserToolSessionApprovalKind = "factory" UserToolSessionApprovalKindMCP UserToolSessionApprovalKind = "mcp" UserToolSessionApprovalKindMemory UserToolSessionApprovalKind = "memory" UserToolSessionApprovalKindRead UserToolSessionApprovalKind = "read" @@ -15159,6 +15400,67 @@ func (a *ServerCommandsAPI) List(ctx context.Context) (*CommandList, error) { return &result, nil } +// Experimental: ServerExtensionsAPI contains experimental APIs that may change or be +// removed. +type ServerExtensionsAPI serverAPI + +// Disable persistently disables extension IDs for future sessions. Active sessions are +// unchanged; use session.extensions.disable to update them. +// +// RPC method: extensions.disable. +// +// Parameters: Source-qualified extension identifiers to persistently disable for future +// sessions. +func (a *ServerExtensionsAPI) Disable(ctx context.Context, params *DiscoveredExtensionsDisableRequest) (*ExtensionsDisableResult, error) { + raw, err := a.client.Request(ctx, "extensions.disable", params) + if err != nil { + return nil, err + } + var result ExtensionsDisableResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Discovers user and enabled installed-plugin extensions from persisted Copilot home state, +// including enablement preferences. Launch-scoped additional plugins are not included. +// +// RPC method: extensions.discover. +// +// Returns: Extensions discovered from persisted Copilot home state and their effective +// loading mode. Launch-scoped additional plugins are not included. +func (a *ServerExtensionsAPI) Discover(ctx context.Context) (*DiscoveredExtensions, error) { + raw, err := a.client.Request(ctx, "extensions.discover", nil) + if err != nil { + return nil, err + } + var result DiscoveredExtensions + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Enable persistently enables extension IDs for future sessions. Active sessions are +// unchanged; use session.extensions.enable to update them. +// +// RPC method: extensions.enable. +// +// Parameters: Source-qualified extension identifiers to persistently enable for future +// sessions. +func (a *ServerExtensionsAPI) Enable(ctx context.Context, params *DiscoveredExtensionsEnableRequest) (*ExtensionsEnableResult, error) { + raw, err := a.client.Request(ctx, "extensions.enable", params) + if err != nil { + return nil, err + } + var result ExtensionsEnableResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: ServerInstructionsAPI contains experimental APIs that may change or be // removed. type ServerInstructionsAPI serverAPI @@ -16416,6 +16718,7 @@ type ServerRPC struct { AgentRegistry *ServerAgentRegistryAPI Agents *ServerAgentsAPI Commands *ServerCommandsAPI + Extensions *ServerExtensionsAPI Instructions *ServerInstructionsAPI LlmInference *ServerLlmInferenceAPI MCP *ServerMCPAPI @@ -16458,6 +16761,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.AgentRegistry = (*ServerAgentRegistryAPI)(&r.common) r.Agents = (*ServerAgentsAPI)(&r.common) r.Commands = (*ServerCommandsAPI)(&r.common) + r.Extensions = (*ServerExtensionsAPI)(&r.common) r.Instructions = (*ServerInstructionsAPI)(&r.common) r.LlmInference = (*ServerLlmInferenceAPI)(&r.common) r.MCP = (*ServerMCPAPI)(&r.common) @@ -16835,6 +17139,32 @@ func (a *AgentAPI) Select(ctx context.Context, params *AgentSelectRequest) (*Age return &result, nil } +// SetPrompt sets an in-memory authored prompt override for an available agent. For built-in +// agents, this replaces only the static base prompt while preserving runtime-owned dynamic +// prompt composition and behavior. The special `general-purpose` agent is not overrideable. +// Overrides are not persisted; resumed and forked sessions start without them, so the host +// must re-apply them. +// +// RPC method: session.agent.setPrompt. +// +// Parameters: An in-memory authored prompt override for an available agent. +func (a *AgentAPI) SetPrompt(ctx context.Context, params *AgentSetPromptRequest) (*SessionAgentSetPromptResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["id"] = params.ID + req["prompt"] = params.Prompt + } + raw, err := a.client.Request(ctx, "session.agent.setPrompt", req) + if err != nil { + return nil, err + } + var result SessionAgentSetPromptResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: CanvasAPI contains experimental APIs that may change or be removed. type CanvasAPI sessionAPI @@ -17247,6 +17577,7 @@ func (a *DebugAPI) CollectLogs(ctx context.Context, params *DebugCollectLogsRequ type EventLogAPI sessionAPI // Reads a batch of session events from a cursor, optionally waiting for new events. +// Supports tail-first reads via `direction: backward`. // // RPC method: session.eventLog.read. // @@ -17258,12 +17589,18 @@ type EventLogAPI sessionAPI func (a *EventLogAPI) Read(ctx context.Context, params *EventLogReadRequest) (*EventsReadResult, error) { req := map[string]any{"sessionId": a.sessionID} if params != nil { + if params.AgentIDs != nil { + req["agentIds"] = params.AgentIDs + } if params.AgentScope != nil { req["agentScope"] = *params.AgentScope } if params.Cursor != nil { req["cursor"] = *params.Cursor } + if params.Direction != nil { + req["direction"] = *params.Direction + } if params.IncludeEphemeral != nil { req["includeEphemeral"] = *params.IncludeEphemeral } @@ -18614,6 +18951,33 @@ func (s *MCPAPI) Headers() *MCPHeadersAPI { // Experimental: MCPOauthAPI contains experimental APIs that may change or be removed. type MCPOauthAPI sessionAPI +// AuthenticationStateChanged notifies the session that MCP OAuth authentication succeeded +// and updated credentials were persisted, so cached tool definitions can be refreshed. +// +// RPC method: session.mcp.oauth.authenticationStateChanged. +// +// Parameters: Identifies the MCP server whose persisted OAuth credentials were updated. +func (a *MCPOauthAPI) AuthenticationStateChanged(ctx context.Context, params *MCPOauthAuthenticationStateChangedRequest) (*SessionMCPOauthAuthenticationStateChangedResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.RefreshSessionToken != nil { + req["refreshSessionToken"] = *params.RefreshSessionToken + } + if params.ServerName != nil { + req["serverName"] = *params.ServerName + } + } + raw, err := a.client.Request(ctx, "session.mcp.oauth.authenticationStateChanged", req) + if err != nil { + return nil, err + } + var result SessionMCPOauthAuthenticationStateChangedResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // HandlePendingRequest resolves a pending MCP OAuth request with a host-provided token or // cancellation. The pending request is emitted as mcp.oauth_required with the data // necessary to authorize the request. diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 16cf00bab..fdc76729b 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -1906,6 +1906,12 @@ func unmarshalUserToolSessionApproval(data []byte) (UserToolSessionApproval, err return nil, err } return &d, nil + case UserToolSessionApprovalKindFactory: + var d UserToolSessionApprovalFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case UserToolSessionApprovalKindMCP: var d UserToolSessionApprovalMCP if err := json.Unmarshal(data, &d); err != nil { @@ -1990,6 +1996,17 @@ func (r UserToolSessionApprovalExtensionPermissionAccess) MarshalJSON() ([]byte, }) } +func (r UserToolSessionApprovalFactory) MarshalJSON() ([]byte, error) { + type alias UserToolSessionApprovalFactory + return json.Marshal(struct { + Kind UserToolSessionApprovalKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r UserToolSessionApprovalMCP) MarshalJSON() ([]byte, error) { type alias UserToolSessionApprovalMCP return json.Marshal(struct { @@ -2131,6 +2148,12 @@ func unmarshalPermissionDecisionApproveForLocationApproval(data []byte) (Permiss return nil, err } return &d, nil + case PermissionDecisionApproveForLocationApprovalKindFactory: + var d PermissionDecisionApproveForLocationApprovalFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case PermissionDecisionApproveForLocationApprovalKindMCP: var d PermissionDecisionApproveForLocationApprovalMCP if err := json.Unmarshal(data, &d); err != nil { @@ -2221,6 +2244,17 @@ func (r PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess) M }) } +func (r PermissionDecisionApproveForLocationApprovalFactory) MarshalJSON() ([]byte, error) { + type alias PermissionDecisionApproveForLocationApprovalFactory + return json.Marshal(struct { + Kind PermissionDecisionApproveForLocationApprovalKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r PermissionDecisionApproveForLocationApprovalMCP) MarshalJSON() ([]byte, error) { type alias PermissionDecisionApproveForLocationApprovalMCP return json.Marshal(struct { @@ -2344,6 +2378,12 @@ func unmarshalPermissionDecisionApproveForSessionApproval(data []byte) (Permissi return nil, err } return &d, nil + case PermissionDecisionApproveForSessionApprovalKindFactory: + var d PermissionDecisionApproveForSessionApprovalFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case PermissionDecisionApproveForSessionApprovalKindMCP: var d PermissionDecisionApproveForSessionApprovalMCP if err := json.Unmarshal(data, &d); err != nil { @@ -2434,6 +2474,17 @@ func (r PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess) Ma }) } +func (r PermissionDecisionApproveForSessionApprovalFactory) MarshalJSON() ([]byte, error) { + type alias PermissionDecisionApproveForSessionApprovalFactory + return json.Marshal(struct { + Kind PermissionDecisionApproveForSessionApprovalKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r PermissionDecisionApproveForSessionApprovalMCP) MarshalJSON() ([]byte, error) { type alias PermissionDecisionApproveForSessionApprovalMCP return json.Marshal(struct { @@ -2687,6 +2738,12 @@ func unmarshalPermissionsLocationsAddToolApprovalDetails(data []byte) (Permissio return nil, err } return &d, nil + case PermissionsLocationsAddToolApprovalDetailsKindFactory: + var d PermissionsLocationsAddToolApprovalDetailsFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case PermissionsLocationsAddToolApprovalDetailsKindMCP: var d PermissionsLocationsAddToolApprovalDetailsMCP if err := json.Unmarshal(data, &d); err != nil { @@ -2777,6 +2834,17 @@ func (r PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess) Mar }) } +func (r PermissionsLocationsAddToolApprovalDetailsFactory) MarshalJSON() ([]byte, error) { + type alias PermissionsLocationsAddToolApprovalDetailsFactory + return json.Marshal(struct { + Kind PermissionsLocationsAddToolApprovalDetailsKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r PermissionsLocationsAddToolApprovalDetailsMCP) MarshalJSON() ([]byte, error) { type alias PermissionsLocationsAddToolApprovalDetailsMCP return json.Marshal(struct { diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index e472ffe67..eae934e93 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -1352,6 +1352,12 @@ func unmarshalSystemNotification(data []byte) (SystemNotification, error) { return nil, err } return &d, nil + case SystemNotificationTypeFactoryCompleted: + var d SystemNotificationFactoryCompleted + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case SystemNotificationTypeInstructionDiscovered: var d SystemNotificationInstructionDiscovered if err := json.Unmarshal(data, &d); err != nil { @@ -1420,6 +1426,17 @@ func (r SystemNotificationAgentIdle) MarshalJSON() ([]byte, error) { }) } +func (r SystemNotificationFactoryCompleted) MarshalJSON() ([]byte, error) { + type alias SystemNotificationFactoryCompleted + return json.Marshal(struct { + Type SystemNotificationType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r SystemNotificationInstructionDiscovered) MarshalJSON() ([]byte, error) { type alias SystemNotificationInstructionDiscovered return json.Marshal(struct { @@ -1526,6 +1543,12 @@ func unmarshalPermissionRequest(data []byte) (PermissionRequest, error) { return nil, err } return &d, nil + case PermissionRequestKindFactory: + var d PermissionRequestFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case PermissionRequestKindHook: var d PermissionRequestHook if err := json.Unmarshal(data, &d); err != nil { @@ -1617,6 +1640,17 @@ func (r PermissionRequestExtensionPermissionAccess) MarshalJSON() ([]byte, error }) } +func (r PermissionRequestFactory) MarshalJSON() ([]byte, error) { + type alias PermissionRequestFactory + return json.Marshal(struct { + Kind PermissionRequestKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r PermissionRequestHook) MarshalJSON() ([]byte, error) { type alias PermissionRequestHook return json.Marshal(struct { @@ -1731,6 +1765,12 @@ func unmarshalPermissionPromptRequest(data []byte) (PermissionPromptRequest, err return nil, err } return &d, nil + case PermissionPromptRequestKindFactory: + var d PermissionPromptRequestFactory + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case PermissionPromptRequestKindHook: var d PermissionPromptRequestHook if err := json.Unmarshal(data, &d); err != nil { @@ -1833,6 +1873,17 @@ func (r PermissionPromptRequestExtensionPermissionAccess) MarshalJSON() ([]byte, }) } +func (r PermissionPromptRequestFactory) MarshalJSON() ([]byte, error) { + type alias PermissionPromptRequestFactory + return json.Marshal(struct { + Kind PermissionPromptRequestKind `json:"kind"` + alias + }{ + Kind: r.Kind(), + alias: alias(r), + }) +} + func (r PermissionPromptRequestHook) MarshalJSON() ([]byte, error) { type alias PermissionPromptRequestHook return json.Marshal(struct { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 54b46e25a..6855956b9 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -227,6 +227,10 @@ func (*AssistantReasoningData) Type() SessionEventType { return SessionEventType type AssistantMessageData struct { // Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. APICallID *string `json:"apiCallId,omitempty"` + // Total messages the model call's response was split into, one per reasoning boundary. Absent for a single-message response; the last chunk is the one where chunkIndex is chunkCount - 1. + ChunkCount *int64 `json:"chunkCount,omitempty"` + // Zero-based position of this message within its model call's response. Absent when the response was not split into chunks. + ChunkIndex *int64 `json:"chunkIndex,omitempty"` // Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. // Experimental: Citations is part of an experimental API and may change or be removed. Citations *Citations `json:"citations,omitempty"` @@ -1577,6 +1581,8 @@ type SessionStartData struct { CopilotVersion string `json:"copilotVersion"` // When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` + // Per-session GitHub MCP override persisted for cold resume + GitHubMCPToolConfig *GitHubMCPToolConfig `json:"githubMcpToolConfig,omitempty"` // Identifier of the software producing the events (e.g., "copilot-agent") Producer string `json:"producer"` // Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") @@ -2528,6 +2534,26 @@ type ExtensionsLoadedExtension struct { Status ExtensionsLoadedExtensionStatus `json:"status"` } +// A declared phase shown in a factory permission prompt. +type FactoryPermissionPhase struct { + // Optional phase detail + Detail *string `json:"detail,omitempty"` + // Phase title + Title string `json:"title"` +} + +// Per-session configuration for the built-in GitHub MCP server +type GitHubMCPToolConfig struct { + // Additional GitHub MCP tools requested by the session + AdditionalTools []string `json:"additionalTools,omitzero"` + // Additional GitHub MCP toolsets requested by the session + AdditionalToolsets []string `json:"additionalToolsets,omitzero"` + // Whether to use the read-write endpoint and request all toolsets + EnableAllTools *bool `json:"enableAllTools,omitempty"` + // Whether to request the GitHub MCP insiders build + EnableInsidersMode *bool `json:"enableInsidersMode,omitempty"` +} + // Repository context for the handed-off session type HandoffRepository struct { // Git branch name, if applicable @@ -2755,6 +2781,46 @@ func (PermissionPromptRequestExtensionPermissionAccess) Kind() PermissionPromptR return PermissionPromptRequestKindExtensionPermissionAccess } +// Factory run or authoring permission prompt +type PermissionPromptRequestFactory struct { + // Canonical key used for scoped factory approvals + ApprovalKey string `json:"approvalKey"` + // Auto-approval judge information for this request; present only when auto mode is enabled. + // Experimental: AutoApproval is part of an experimental API and may change or be removed. + AutoApproval *PermissionAutoApproval `json:"autoApproval,omitempty"` + // Whether this factory is eligible for persistent approval + CanPersistApproval bool `json:"canPersistApproval"` + DeclaredMaxAiCredits *float64 `json:"declaredMaxAiCredits,omitempty"` + DeclaredMaxConcurrentSubagents *int64 `json:"declaredMaxConcurrentSubagents,omitempty"` + DeclaredMaxTotalSubagents *int64 `json:"declaredMaxTotalSubagents,omitempty"` + DeclaredTimeoutSeconds *float64 `json:"declaredTimeoutSeconds,omitempty"` + // Factory description + Description string `json:"description"` + // Whether managed policy requires a human response and forbids host auto-approval + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` + // Effective AI-credit limit; omitted means unlimited + MaxAiCredits *float64 `json:"maxAiCredits,omitempty"` + // Effective concurrent-subagent limit; omitted means unlimited + MaxConcurrentSubagents *int64 `json:"maxConcurrentSubagents,omitempty"` + // Effective total-subagent limit; omitted means unlimited + MaxTotalSubagents *int64 `json:"maxTotalSubagents,omitempty"` + // Factory name + Name string `json:"name"` + // Factory operation, either run or author + Operation FactoryPermissionOperation `json:"operation"` + // Declared factory phases + Phases []FactoryPermissionPhase `json:"phases"` + // Effective active-time limit in seconds; omitted means unlimited + TimeoutSeconds *float64 `json:"timeoutSeconds,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionPromptRequestFactory) permissionPromptRequest() {} +func (PermissionPromptRequestFactory) Kind() PermissionPromptRequestKind { + return PermissionPromptRequestKindFactory +} + // Hook confirmation permission prompt type PermissionPromptRequestHook struct { // Auto-approval judge information for this request; present only when auto mode is enabled. @@ -2983,6 +3049,43 @@ func (PermissionRequestExtensionPermissionAccess) Kind() PermissionRequestKind { return PermissionRequestKindExtensionPermissionAccess } +// Factory run or authoring permission request +type PermissionRequestFactory struct { + // Canonical key used for scoped factory approvals + ApprovalKey string `json:"approvalKey"` + // Whether this factory is eligible for persistent approval + CanPersistApproval bool `json:"canPersistApproval"` + DeclaredMaxAiCredits *float64 `json:"declaredMaxAiCredits,omitempty"` + DeclaredMaxConcurrentSubagents *int64 `json:"declaredMaxConcurrentSubagents,omitempty"` + DeclaredMaxTotalSubagents *int64 `json:"declaredMaxTotalSubagents,omitempty"` + DeclaredTimeoutSeconds *float64 `json:"declaredTimeoutSeconds,omitempty"` + // Factory description + Description string `json:"description"` + // When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + ManagedApprovalRequired *bool `json:"managedApprovalRequired,omitempty"` + // Effective AI-credit limit; omitted means unlimited + MaxAiCredits *float64 `json:"maxAiCredits,omitempty"` + // Effective concurrent-subagent limit; omitted means unlimited + MaxConcurrentSubagents *int64 `json:"maxConcurrentSubagents,omitempty"` + // Effective total-subagent limit; omitted means unlimited + MaxTotalSubagents *int64 `json:"maxTotalSubagents,omitempty"` + // Factory name + Name string `json:"name"` + // Factory operation, either run or author + Operation FactoryPermissionOperation `json:"operation"` + // Declared factory phases + Phases []FactoryPermissionPhase `json:"phases"` + // Effective active-time limit in seconds; omitted means unlimited + TimeoutSeconds *float64 `json:"timeoutSeconds,omitempty"` + // Tool call ID that triggered this permission request + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (PermissionRequestFactory) permissionRequest() {} +func (PermissionRequestFactory) Kind() PermissionRequestKind { + return PermissionRequestKindFactory +} + // Hook confirmation permission request type PermissionRequestHook struct { // Optional message from the hook explaining why confirmation is needed @@ -3526,6 +3629,35 @@ func (SystemNotificationAgentIdle) Type() SystemNotificationType { return SystemNotificationTypeAgentIdle } +// System notification metadata for a factory execution attempt that reached a terminal state. +type SystemNotificationFactoryCompleted struct { + // Execution attempt that reached this terminal state. + Attempt int64 `json:"attempt"` + // Consumed AI usage in nano-AIU. + ConsumedNanoAiu int64 `json:"consumedNanoAiu"` + // Subagents consumed by the run across all attempts. + ConsumedSubagents int64 `json:"consumedSubagents"` + // Accumulated active execution time in milliseconds. + ElapsedMs int64 `json:"elapsedMs"` + // Persisted factory name. + FactoryName string `json:"factoryName"` + // Machine-readable terminal failure details, when present. + Failure any `json:"failure,omitempty"` + // Bounded prompt-safe preview of the completed result. + ResultPreview *string `json:"resultPreview,omitempty"` + // Actionable run_factory resume guidance for a resource-limit failure. + RetryGuidance *string `json:"retryGuidance,omitempty"` + // Factory run identifier. + RunID string `json:"runId"` + // Terminal status reached by this execution attempt. + Status SystemNotificationFactoryCompletedStatus `json:"status"` +} + +func (SystemNotificationFactoryCompleted) systemNotification() {} +func (SystemNotificationFactoryCompleted) Type() SystemNotificationType { + return SystemNotificationTypeFactoryCompleted +} + // System notification metadata for an instruction file discovered during tool access, including source, trigger file, and tool. type SystemNotificationInstructionDiscovered struct { // Human-readable label for the timeline (e.g., 'AGENTS.md from packages/billing/') @@ -4164,6 +4296,16 @@ const ( ExtensionsLoadedExtensionStatusStarting ExtensionsLoadedExtensionStatus = "starting" ) +// Operation gated by a factory permission request. +type FactoryPermissionOperation string + +const ( + // Authoring a factory, which writes JavaScript into a session-scoped extension and loads it. + FactoryPermissionOperationAuthor FactoryPermissionOperation = "author" + // Running a registered factory, which spends subagents, active time, and AI credits under the approved limits. + FactoryPermissionOperationRun FactoryPermissionOperation = "run" +) + // Origin type of the session being handed off type HandoffSourceType string @@ -4352,6 +4494,7 @@ const ( PermissionPromptRequestKindCustomTool PermissionPromptRequestKind = "custom-tool" PermissionPromptRequestKindExtensionManagement PermissionPromptRequestKind = "extension-management" PermissionPromptRequestKindExtensionPermissionAccess PermissionPromptRequestKind = "extension-permission-access" + PermissionPromptRequestKindFactory PermissionPromptRequestKind = "factory" PermissionPromptRequestKindHook PermissionPromptRequestKind = "hook" PermissionPromptRequestKindMCP PermissionPromptRequestKind = "mcp" PermissionPromptRequestKindMemory PermissionPromptRequestKind = "memory" @@ -4380,6 +4523,7 @@ const ( PermissionRequestKindCustomTool PermissionRequestKind = "custom-tool" PermissionRequestKindExtensionManagement PermissionRequestKind = "extension-management" PermissionRequestKindExtensionPermissionAccess PermissionRequestKind = "extension-permission-access" + PermissionRequestKindFactory PermissionRequestKind = "factory" PermissionRequestKindHook PermissionRequestKind = "hook" PermissionRequestKindMCP PermissionRequestKind = "mcp" PermissionRequestKindMemory PermissionRequestKind = "memory" @@ -4511,12 +4655,27 @@ const ( SystemNotificationAgentCompletedStatusFailed SystemNotificationAgentCompletedStatus = "failed" ) +// Terminal status reached by a factory execution attempt. +type SystemNotificationFactoryCompletedStatus string + +const ( + // The factory was cancelled. + SystemNotificationFactoryCompletedStatusCancelled SystemNotificationFactoryCompletedStatus = "cancelled" + // The factory completed successfully. + SystemNotificationFactoryCompletedStatusCompleted SystemNotificationFactoryCompletedStatus = "completed" + // The factory failed. + SystemNotificationFactoryCompletedStatusError SystemNotificationFactoryCompletedStatus = "error" + // The factory was halted. + SystemNotificationFactoryCompletedStatusHalted SystemNotificationFactoryCompletedStatus = "halted" +) + // Type discriminator for SystemNotification. type SystemNotificationType string const ( SystemNotificationTypeAgentCompleted SystemNotificationType = "agent_completed" SystemNotificationTypeAgentIdle SystemNotificationType = "agent_idle" + SystemNotificationTypeFactoryCompleted SystemNotificationType = "factory_completed" SystemNotificationTypeInstructionDiscovered SystemNotificationType = "instruction_discovered" SystemNotificationTypeNewInboxMessage SystemNotificationType = "new_inbox_message" SystemNotificationTypeShellCompleted SystemNotificationType = "shell_completed" diff --git a/go/zsession_events.go b/go/zsession_events.go index c79814c9e..a6bd1ed73 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -105,6 +105,8 @@ type ( ExtensionsLoadedExtensionStatus = rpc.ExtensionsLoadedExtensionStatus ExternalToolCompletedData = rpc.ExternalToolCompletedData ExternalToolRequestedData = rpc.ExternalToolRequestedData + FactoryPermissionOperation = rpc.FactoryPermissionOperation + FactoryPermissionPhase = rpc.FactoryPermissionPhase FactoryRunUpdatedData = rpc.FactoryRunUpdatedData GitHubRepoRef = rpc.GitHubRepoRef HandoffRepository = rpc.HandoffRepository @@ -168,6 +170,7 @@ type ( PermissionPromptRequestCustomTool = rpc.PermissionPromptRequestCustomTool PermissionPromptRequestExtensionManagement = rpc.PermissionPromptRequestExtensionManagement PermissionPromptRequestExtensionPermissionAccess = rpc.PermissionPromptRequestExtensionPermissionAccess + PermissionPromptRequestFactory = rpc.PermissionPromptRequestFactory PermissionPromptRequestHook = rpc.PermissionPromptRequestHook PermissionPromptRequestKind = rpc.PermissionPromptRequestKind PermissionPromptRequestMCP = rpc.PermissionPromptRequestMCP @@ -183,6 +186,7 @@ type ( PermissionRequestedData = rpc.PermissionRequestedData PermissionRequestExtensionManagement = rpc.PermissionRequestExtensionManagement PermissionRequestExtensionPermissionAccess = rpc.PermissionRequestExtensionPermissionAccess + PermissionRequestFactory = rpc.PermissionRequestFactory PermissionRequestHook = rpc.PermissionRequestHook PermissionRequestKind = rpc.PermissionRequestKind PermissionRequestMCP = rpc.PermissionRequestMCP @@ -298,6 +302,8 @@ type ( SystemNotificationAgentCompletedStatus = rpc.SystemNotificationAgentCompletedStatus SystemNotificationAgentIdle = rpc.SystemNotificationAgentIdle SystemNotificationData = rpc.SystemNotificationData + SystemNotificationFactoryCompleted = rpc.SystemNotificationFactoryCompleted + SystemNotificationFactoryCompletedStatus = rpc.SystemNotificationFactoryCompletedStatus SystemNotificationInstructionDiscovered = rpc.SystemNotificationInstructionDiscovered SystemNotificationNewInboxMessage = rpc.SystemNotificationNewInboxMessage SystemNotificationShellCompleted = rpc.SystemNotificationShellCompleted @@ -353,6 +359,7 @@ type ( UserToolSessionApprovalCustomTool = rpc.UserToolSessionApprovalCustomTool UserToolSessionApprovalExtensionManagement = rpc.UserToolSessionApprovalExtensionManagement UserToolSessionApprovalExtensionPermissionAccess = rpc.UserToolSessionApprovalExtensionPermissionAccess + UserToolSessionApprovalFactory = rpc.UserToolSessionApprovalFactory UserToolSessionApprovalKind = rpc.UserToolSessionApprovalKind UserToolSessionApprovalMCP = rpc.UserToolSessionApprovalMCP UserToolSessionApprovalMemory = rpc.UserToolSessionApprovalMemory @@ -451,6 +458,8 @@ const ( ExtensionsLoadedExtensionStatusFailed = rpc.ExtensionsLoadedExtensionStatusFailed ExtensionsLoadedExtensionStatusRunning = rpc.ExtensionsLoadedExtensionStatusRunning ExtensionsLoadedExtensionStatusStarting = rpc.ExtensionsLoadedExtensionStatusStarting + FactoryPermissionOperationAuthor = rpc.FactoryPermissionOperationAuthor + FactoryPermissionOperationRun = rpc.FactoryPermissionOperationRun HandoffSourceTypeLocal = rpc.HandoffSourceTypeLocal HandoffSourceTypeRemote = rpc.HandoffSourceTypeRemote ManagedSettingsEnforcedActionBypassPermissionsBlocked = rpc.ManagedSettingsEnforcedActionBypassPermissionsBlocked @@ -510,6 +519,7 @@ const ( PermissionPromptRequestKindCustomTool = rpc.PermissionPromptRequestKindCustomTool PermissionPromptRequestKindExtensionManagement = rpc.PermissionPromptRequestKindExtensionManagement PermissionPromptRequestKindExtensionPermissionAccess = rpc.PermissionPromptRequestKindExtensionPermissionAccess + PermissionPromptRequestKindFactory = rpc.PermissionPromptRequestKindFactory PermissionPromptRequestKindHook = rpc.PermissionPromptRequestKindHook PermissionPromptRequestKindMCP = rpc.PermissionPromptRequestKindMCP PermissionPromptRequestKindMemory = rpc.PermissionPromptRequestKindMemory @@ -523,6 +533,7 @@ const ( PermissionRequestKindCustomTool = rpc.PermissionRequestKindCustomTool PermissionRequestKindExtensionManagement = rpc.PermissionRequestKindExtensionManagement PermissionRequestKindExtensionPermissionAccess = rpc.PermissionRequestKindExtensionPermissionAccess + PermissionRequestKindFactory = rpc.PermissionRequestKindFactory PermissionRequestKindHook = rpc.PermissionRequestKindHook PermissionRequestKindMCP = rpc.PermissionRequestKindMCP PermissionRequestKindMemory = rpc.PermissionRequestKindMemory @@ -692,8 +703,13 @@ const ( SystemMessageRoleSystem = rpc.SystemMessageRoleSystem SystemNotificationAgentCompletedStatusCompleted = rpc.SystemNotificationAgentCompletedStatusCompleted SystemNotificationAgentCompletedStatusFailed = rpc.SystemNotificationAgentCompletedStatusFailed + SystemNotificationFactoryCompletedStatusCancelled = rpc.SystemNotificationFactoryCompletedStatusCancelled + SystemNotificationFactoryCompletedStatusCompleted = rpc.SystemNotificationFactoryCompletedStatusCompleted + SystemNotificationFactoryCompletedStatusError = rpc.SystemNotificationFactoryCompletedStatusError + SystemNotificationFactoryCompletedStatusHalted = rpc.SystemNotificationFactoryCompletedStatusHalted SystemNotificationTypeAgentCompleted = rpc.SystemNotificationTypeAgentCompleted SystemNotificationTypeAgentIdle = rpc.SystemNotificationTypeAgentIdle + SystemNotificationTypeFactoryCompleted = rpc.SystemNotificationTypeFactoryCompleted SystemNotificationTypeInstructionDiscovered = rpc.SystemNotificationTypeInstructionDiscovered SystemNotificationTypeNewInboxMessage = rpc.SystemNotificationTypeNewInboxMessage SystemNotificationTypeShellCompleted = rpc.SystemNotificationTypeShellCompleted @@ -726,6 +742,7 @@ const ( UserToolSessionApprovalKindCustomTool = rpc.UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKindExtensionManagement = rpc.UserToolSessionApprovalKindExtensionManagement UserToolSessionApprovalKindExtensionPermissionAccess = rpc.UserToolSessionApprovalKindExtensionPermissionAccess + UserToolSessionApprovalKindFactory = rpc.UserToolSessionApprovalKindFactory UserToolSessionApprovalKindMCP = rpc.UserToolSessionApprovalKindMCP UserToolSessionApprovalKindMemory = rpc.UserToolSessionApprovalKindMemory UserToolSessionApprovalKindRead = rpc.UserToolSessionApprovalKindRead diff --git a/java/pom.xml b/java/pom.xml index 24765bad5..1e5c76beb 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.77 + ^1.0.78-2 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index 3ddbf772b..a071da4d7 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", - "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", + "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.77", - "@github/copilot-darwin-x64": "1.0.77", - "@github/copilot-linux-arm64": "1.0.77", - "@github/copilot-linux-x64": "1.0.77", - "@github/copilot-linuxmusl-arm64": "1.0.77", - "@github/copilot-linuxmusl-x64": "1.0.77", - "@github/copilot-win32-arm64": "1.0.77", - "@github/copilot-win32-x64": "1.0.77" + "@github/copilot-darwin-arm64": "1.0.78-2", + "@github/copilot-darwin-x64": "1.0.78-2", + "@github/copilot-linux-arm64": "1.0.78-2", + "@github/copilot-linux-x64": "1.0.78-2", + "@github/copilot-linuxmusl-arm64": "1.0.78-2", + "@github/copilot-linuxmusl-x64": "1.0.78-2", + "@github/copilot-win32-arm64": "1.0.78-2", + "@github/copilot-win32-x64": "1.0.78-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", - "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", + "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", - "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", + "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", - "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", + "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", - "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", + "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", - "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", + "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", - "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", + "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", - "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", + "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", - "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", + "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index f3a144cb1..0c8287d4c 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java b/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java index 6c1a87324..fee236ed2 100644 --- a/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java @@ -53,6 +53,10 @@ public record AssistantMessageEventData( @JsonProperty("encryptedContent") String encryptedContent, /** Generation phase for phased-output models (e.g., thinking vs. response phases) */ @JsonProperty("phase") String phase, + /** Zero-based position of this message within its model call's response. Absent when the response was not split into chunks. */ + @JsonProperty("chunkIndex") Long chunkIndex, + /** Total messages the model call's response was split into, one per reasoning boundary. Absent for a single-message response; the last chunk is the one where chunkIndex is chunkCount - 1. */ + @JsonProperty("chunkCount") Long chunkCount, /** Actual output token count from the API response (completion_tokens), used for accurate token accounting */ @JsonProperty("outputTokens") Long outputTokens, /** CAPI interaction ID for correlating this message with upstream telemetry */ diff --git a/java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java b/java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java new file mode 100644 index 000000000..afa69b985 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Per-session configuration for the built-in GitHub MCP server + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record GitHubMcpToolConfig( + /** Whether to use the read-write endpoint and request all toolsets */ + @JsonProperty("enableAllTools") Boolean enableAllTools, + /** Additional GitHub MCP toolsets requested by the session */ + @JsonProperty("additionalToolsets") List additionalToolsets, + /** Additional GitHub MCP tools requested by the session */ + @JsonProperty("additionalTools") List additionalTools, + /** Whether to request the GitHub MCP insiders build */ + @JsonProperty("enableInsidersMode") Boolean enableInsidersMode +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java index 4beb487c3..bf8b4e91c 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java @@ -59,6 +59,8 @@ public record SessionStartEventData( @JsonProperty("sessionLimits") SessionLimitsConfig sessionLimits, /** Working directory and git context at session start */ @JsonProperty("context") WorkingDirectoryContext context, + /** Per-session GitHub MCP override persisted for cold resume */ + @JsonProperty("githubMcpToolConfig") GitHubMcpToolConfig gitHubMcpToolConfig, /** Whether the session was already in use by another client at start time */ @JsonProperty("alreadyInUse") Boolean alreadyInUse, /** Whether this session supports remote steering via GitHub */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java new file mode 100644 index 000000000..7bb2531fe --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Discovered extension metadata and persistent enablement state. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record DiscoveredExtension( + /** Source-qualified ID accepted by both server and session extension enablement methods */ + @JsonProperty("id") String id, + /** Human-readable extension name */ + @JsonProperty("name") String name, + /** Absolute path to the extension entry module, suitable for revealing it in a file manager */ + @JsonProperty("path") String path, + /** Discovery source */ + @JsonProperty("source") DiscoveredExtensionSource source, + /** Whether this extension's persistent per-ID preference is enabled */ + @JsonProperty("enabled") Boolean enabled, + /** Containing plugin metadata for plugin-contributed extensions */ + @JsonProperty("plugin") DiscoveredExtensionPlugin plugin +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java new file mode 100644 index 000000000..23bc32778 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Effective extension loading and agent-management mode + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum DiscoveredExtensionMode { + /** The {@code disabled} variant. */ + DISABLED("disabled"), + /** The {@code load_only} variant. */ + LOAD_ONLY("load_only"), + /** The {@code load_and_augment} variant. */ + LOAD_AND_AUGMENT("load_and_augment"); + + private final String value; + DiscoveredExtensionMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static DiscoveredExtensionMode fromValue(String value) { + for (DiscoveredExtensionMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown DiscoveredExtensionMode value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java new file mode 100644 index 000000000..8df0018ef --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Installed plugin that contributes a discovered extension. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record DiscoveredExtensionPlugin( + /** Installed plugin name */ + @JsonProperty("name") String name +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java new file mode 100644 index 000000000..c38225167 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Persisted extension discovery source + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum DiscoveredExtensionSource { + /** The {@code user} variant. */ + USER("user"), + /** The {@code plugin} variant. */ + PLUGIN("plugin"); + + private final String value; + DiscoveredExtensionSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static DiscoveredExtensionSource fromValue(String value) { + for (DiscoveredExtensionSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown DiscoveredExtensionSource value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java b/java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java index 31c1fcab0..20f37bdfa 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. + * Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history (the beginning for a forward read, the tail for a backward read). The fallback page is a fresh boundary snapshot, not a continuation of the requested cursor, so it may overlap already-rendered events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate by event id) before continuing from the returned cursor. * * @since 1.0.0 */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java b/java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java new file mode 100644 index 000000000..1df0ac8f7 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum EventsReadDirection { + /** The {@code forward} variant. */ + FORWARD("forward"), + /** The {@code backward} variant. */ + BACKWARD("backward"); + + private final String value; + EventsReadDirection(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static EventsReadDirection fromValue(String value) { + for (EventsReadDirection v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown EventsReadDirection value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java new file mode 100644 index 000000000..dc4ef9d6c --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Source-qualified extension identifiers to persistently disable for future sessions. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionsDisableParams( + /** Source-qualified user or plugin extension IDs to disable */ + @JsonProperty("ids") List ids +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java new file mode 100644 index 000000000..fa319d7fe --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionsDiscoverResult( + /** Discovered user and enabled installed-plugin extensions from persisted Copilot home state */ + @JsonProperty("extensions") List extensions, + /** Effective extension loading mode. Defaults to load_and_augment when unset. */ + @JsonProperty("mode") DiscoveredExtensionMode mode +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java new file mode 100644 index 000000000..2e4351d3d --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Source-qualified extension identifiers to persistently enable for future sessions. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionsEnableParams( + /** Source-qualified user or plugin extension IDs to enable */ + @JsonProperty("ids") List ids +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java index 3460560b2..be4f2abe2 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java @@ -30,6 +30,8 @@ public record SandboxConfig( /** Whether to inject the Copilot GitHub token as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. Default: false (opt-in). */ @JsonProperty("gitAuth") Boolean gitAuth, /** Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ - @JsonProperty("ghAuth") Boolean ghAuth + @JsonProperty("ghAuth") Boolean ghAuth, + /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). */ + @JsonProperty("allowDevToolCaches") Boolean allowDevToolCaches ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java new file mode 100644 index 000000000..7bc74b441 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code extensions} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerExtensionsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerExtensionsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture discover() { + return caller.invoke("extensions.discover", java.util.Map.of(), ExtensionsDiscoverResult.class); + } + + /** + * Source-qualified extension identifiers to persistently enable for future sessions. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture enable(ExtensionsEnableParams params) { + return caller.invoke("extensions.enable", params, Void.class); + } + + /** + * Source-qualified extension identifiers to persistently disable for future sessions. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture disable(ExtensionsDisableParams params) { + return caller.invoke("extensions.disable", params, Void.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index 033fe8bf3..e66f32737 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -35,6 +35,8 @@ public final class ServerRpc { public final ServerSecretsApi secrets; /** API methods for the {@code mcp} namespace. */ public final ServerMcpApi mcp; + /** API methods for the {@code extensions} namespace. */ + public final ServerExtensionsApi extensions; /** API methods for the {@code plugins} namespace. */ public final ServerPluginsApi plugins; /** API methods for the {@code skills} namespace. */ @@ -70,6 +72,7 @@ public ServerRpc(RpcCaller caller) { this.account = new ServerAccountApi(caller); this.secrets = new ServerSecretsApi(caller); this.mcp = new ServerMcpApi(caller); + this.extensions = new ServerExtensionsApi(caller); this.plugins = new ServerPluginsApi(caller); this.skills = new ServerSkillsApi(caller); this.agents = new ServerAgentsApi(caller); diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java index 3a1796074..d2499fe3a 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java @@ -59,6 +59,22 @@ public CompletableFuture list(SessionAgentListParams par return caller.invoke("session.agent.list", _p, SessionAgentListResult.class); } + /** + * An in-memory authored prompt override for an available agent. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture setPrompt(SessionAgentSetPromptParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.agent.setPrompt", _p, Void.class); + } + /** * Identifies the target session. * diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java new file mode 100644 index 000000000..4395a195e --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * An in-memory authored prompt override for an available agent. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionAgentSetPromptParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Stable effective agent id. Plugin namespace separators are normalized. */ + @JsonProperty("id") String id, + /** Replacement authored prompt. Empty text is valid. */ + @JsonProperty("prompt") String prompt +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java index ad16d802b..bbc5abb7c 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.github.copilot.CopilotExperimental; +import java.util.List; import javax.annotation.processing.Generated; /** @@ -30,13 +31,17 @@ public record SessionEventLogReadParams( @JsonProperty("cursor") String cursor, /** Maximum number of events to return in this batch (1–1000, default 200). */ @JsonProperty("max") Long max, - /** Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). */ + /** Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. */ @JsonProperty("waitMs") Long waitMs, /** Either '*' to receive all event types, or a non-empty list of event types to receive */ @JsonProperty("types") Object types, /** Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. */ @JsonProperty("agentScope") EventsAgentScope agentScope, - /** When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). */ + /** Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. */ + @JsonProperty("agentIds") List agentIds, + /** Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. */ + @JsonProperty("direction") EventsReadDirection direction, + /** When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. */ @JsonProperty("includeEphemeral") Boolean includeEphemeral ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java index 240c385c0..767acc879 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java @@ -25,13 +25,13 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SessionEventLogReadResult( - /** Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. */ + /** Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. */ @JsonProperty("events") List events, - /** Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. */ + /** Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). */ @JsonProperty("cursor") String cursor, - /** True when the read returned `max` events and more events are available immediately. When false, the next read with a non-zero `waitMs` will block until a new event arrives or the wait expires. */ + /** True when more events are available in the read's direction. For a forward read, true means the batch returned `max` events and more are available immediately. For a backward read, true means older persisted events remain before the returned window. */ @JsonProperty("hasMore") Boolean hasMore, - /** Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. */ + /** Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history. For a forward read the fallback starts from the beginning of the remaining history; for a backward read it falls back to the tail (the newest window). Because the fallback page is a fresh boundary snapshot rather than a continuation of the requested cursor, it may overlap events the consumer has already rendered — a backward fallback to the tail in particular can repeat the newest window. On 'expired', consumers should reset or rebase their local pagination state (or deduplicate by event id) before continuing from the returned cursor rather than blindly appending/prepending the fallback page. */ @JsonProperty("cursorStatus") EventsCursorStatus cursorStatus ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java index 4fb92cf14..1fdb292f8 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java @@ -46,6 +46,22 @@ public CompletableFuture handlePendin return caller.invoke("session.mcp.oauth.handlePendingRequest", _p, SessionMcpOauthHandlePendingRequestResult.class); } + /** + * Identifies the MCP server whose persisted OAuth credentials were updated. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture authenticationStateChanged(SessionMcpOauthAuthenticationStateChangedParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.oauth.authenticationStateChanged", _p, Void.class); + } + /** * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. *

diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java new file mode 100644 index 000000000..b773e1bf7 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the MCP server whose persisted OAuth credentials were updated. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionMcpOauthAuthenticationStateChangedParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. */ + @JsonProperty("serverName") String serverName, + /** Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. */ + @JsonProperty("refreshSessionToken") Boolean refreshSessionToken +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java index 7bde47bc8..f31646f76 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java @@ -30,7 +30,7 @@ public record SessionPermissionsSetAllowAllParams( @JsonProperty("mode") PermissionsAllowAllMode mode, /** Legacy full allow-all toggle. Prefer `mode`; when `mode` is omitted, `enabled: true` is treated as `mode: "on"` and any other value is treated as `mode: "off"`. */ @JsonProperty("enabled") Boolean enabled, - /** Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. */ + /** Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. */ @JsonProperty("model") String model, /** Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. */ @JsonProperty("source") PermissionsSetAllowAllSource source diff --git a/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java b/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java index 6363e90ca..bd38d4962 100644 --- a/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java +++ b/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java @@ -180,7 +180,7 @@ void testHandlerReceivesCorrectEventData() { SessionStartEvent startEvent = createSessionStartEvent(); startEvent.setData(new SessionStartEvent.SessionStartEventData("my-session-123", null, null, null, null, null, - null, null, null, null, null, null, null, null, null)); + null, null, null, null, null, null, null, null, null, null)); dispatchEvent(startEvent); AssistantMessageEvent msgEvent = createAssistantMessageEvent("Test content"); @@ -857,7 +857,7 @@ private SessionStartEvent createSessionStartEvent() { private SessionStartEvent createSessionStartEvent(String sessionId) { var event = new SessionStartEvent(); var data = new SessionStartEvent.SessionStartEventData(sessionId, null, null, null, null, null, null, null, - null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null); event.setData(data); return event; } @@ -865,7 +865,7 @@ private SessionStartEvent createSessionStartEvent(String sessionId) { private AssistantMessageEvent createAssistantMessageEvent(String content) { var event = new AssistantMessageEvent(); var data = new AssistantMessageEvent.AssistantMessageEventData(null, null, content, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); event.setData(data); return event; } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 42110e80e..f936a0cbb 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", - "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", + "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.77", - "@github/copilot-darwin-x64": "1.0.77", - "@github/copilot-linux-arm64": "1.0.77", - "@github/copilot-linux-x64": "1.0.77", - "@github/copilot-linuxmusl-arm64": "1.0.77", - "@github/copilot-linuxmusl-x64": "1.0.77", - "@github/copilot-win32-arm64": "1.0.77", - "@github/copilot-win32-x64": "1.0.77" + "@github/copilot-darwin-arm64": "1.0.78-2", + "@github/copilot-darwin-x64": "1.0.78-2", + "@github/copilot-linux-arm64": "1.0.78-2", + "@github/copilot-linux-x64": "1.0.78-2", + "@github/copilot-linuxmusl-arm64": "1.0.78-2", + "@github/copilot-linuxmusl-x64": "1.0.78-2", + "@github/copilot-win32-arm64": "1.0.78-2", + "@github/copilot-win32-x64": "1.0.78-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", - "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", + "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", - "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", + "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", - "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", + "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", - "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", + "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", - "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", + "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", - "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", + "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", - "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", + "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", - "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", + "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 8dbe3ac81..92a80eed6 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 9e6de6ebe..4c0e09f2d 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 1cedf34cc..b45294a5d 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -418,6 +418,32 @@ export type DebugCollectLogsResultKind = | "archive" /** A directory containing redacted files was written. */ | "directory"; +/** + * Persisted extension discovery source + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensionSource". + */ +/** @experimental */ +export type DiscoveredExtensionSource = + /** Extension discovered from the user's extensions directory. */ + | "user" + /** Extension contributed by an installed plugin. */ + | "plugin"; +/** + * Effective extension loading and agent-management mode + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensionMode". + */ +/** @experimental */ +export type DiscoveredExtensionMode = + /** Extensions are not loaded. */ + | "disabled" + /** Extensions are loaded, but the agent cannot create, reload, or manage them. */ + | "load_only" + /** Extensions are loaded and the agent can create, reload, and manage them. */ + | "load_and_augment"; /** * Server transport type: stdio, http, sse (deprecated), or memory * @@ -455,7 +481,19 @@ export type EventsAgentScope = /** Return events from all agents. */ | "all"; /** - * Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. + * Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "EventsReadDirection". + */ +/** @experimental */ +export type EventsReadDirection = + /** Page from the cursor toward newer events (default). */ + | "forward" + /** Tail-first: return the newest events and page toward older events. */ + | "backward"; +/** + * Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history (the beginning for a forward read, the tail for a backward read). The fallback page is a fresh boundary snapshot, not a continuation of the requested cursor, so it may overlap already-rendered events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate by event id) before continuing from the returned cursor. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "EventsCursorStatus". @@ -1561,6 +1599,7 @@ export type PermissionDecisionApproveForSessionApproval = | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement + | PermissionDecisionApproveForSessionApprovalFactory | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess; /** * Approval to persist for this location @@ -1578,6 +1617,7 @@ export type PermissionDecisionApproveForLocationApproval = | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement + | PermissionDecisionApproveForLocationApprovalFactory | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess; /** * Tool approval to persist and apply @@ -1595,6 +1635,7 @@ export type PermissionsLocationsAddToolApprovalDetails = | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement + | PermissionsLocationsAddToolApprovalDetailsFactory | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess; /** * Whether the location is a git repo or directory @@ -3728,6 +3769,23 @@ export interface AgentSelectRequest { export interface AgentSelectResult { agent: AgentInfo; } +/** + * An in-memory authored prompt override for an available agent. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "AgentSetPromptRequest". + */ +/** @experimental */ +export interface AgentSetPromptRequest { + /** + * Stable effective agent id. Plugin namespace separators are normalized. + */ + id: string; + /** + * Replacement authored prompt. Empty text is valid. + */ + prompt: string; +} /** * Optional project paths to include when enumerating agent discovery directories. * @@ -4872,6 +4930,86 @@ export interface DebugCollectLogsSkippedEntry { */ reason: string; } +/** + * Discovered extension metadata and persistent enablement state. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtension". + */ +/** @experimental */ +export interface DiscoveredExtension { + /** + * Source-qualified ID accepted by both server and session extension enablement methods + */ + id: string; + /** + * Human-readable extension name + */ + name: string; + /** + * Absolute path to the extension entry module, suitable for revealing it in a file manager + */ + path: string; + source: DiscoveredExtensionSource; + /** + * Whether this extension's persistent per-ID preference is enabled + */ + enabled: boolean; + plugin?: DiscoveredExtensionPlugin; +} +/** + * Installed plugin that contributes a discovered extension. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensionPlugin". + */ +/** @experimental */ +export interface DiscoveredExtensionPlugin { + /** + * Installed plugin name + */ + name: string; +} +/** + * Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensions". + */ +/** @experimental */ +export interface DiscoveredExtensions { + /** + * Discovered user and enabled installed-plugin extensions from persisted Copilot home state + */ + extensions: DiscoveredExtension[]; + mode: DiscoveredExtensionMode; +} +/** + * Source-qualified extension identifiers to persistently disable for future sessions. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensionsDisableRequest". + */ +/** @experimental */ +export interface DiscoveredExtensionsDisableRequest { + /** + * Source-qualified user or plugin extension IDs to disable + */ + ids: string[]; +} +/** + * Source-qualified extension identifiers to persistently enable for future sessions. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "DiscoveredExtensionsEnableRequest". + */ +/** @experimental */ +export interface DiscoveredExtensionsEnableRequest { + /** + * Source-qualified user or plugin extension IDs to enable + */ + ids: string[]; +} /** * MCP server discovered by `mcp.discover`, with config source, optional plugin source, transport type, and enabled state. * @@ -4942,13 +5080,20 @@ export interface EventLogReadRequest { */ max?: number; /** - * Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). + * Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. */ waitMs?: number; types?: EventLogTypes; agentScope?: EventsAgentScope; /** - * When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). + * Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. + * + * @minItems 1 + */ + agentIds?: [string, ...string[]]; + direction?: EventsReadDirection; + /** + * When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. */ includeEphemeral?: boolean; } @@ -4987,15 +5132,15 @@ export interface EventLogTailResult { /** @experimental */ export interface EventsReadResult { /** - * Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. + * Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. */ events: SessionEvent[]; /** - * Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. + * Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). */ cursor: string; /** - * True when the read returned `max` events and more events are available immediately. When false, the next read with a non-zero `waitMs` will block until a new event arrives or the wait expires. + * True when more events are available in the read's direction. For a forward read, true means the batch returned `max` events and more are available immediately. For a backward read, true means older persisted events remain before the returned window. */ hasMore: boolean; cursorStatus: EventsCursorStatus; @@ -8140,6 +8285,23 @@ export interface McpToolUi { */ visibility?: McpToolUiVisibility[]; } +/** + * Identifies the MCP server whose persisted OAuth credentials were updated. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "McpOauthAuthenticationStateChangedRequest". + */ +/** @experimental */ +export interface McpOauthAuthenticationStateChangedRequest { + /** + * Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. + */ + serverName?: string; + /** + * Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. + */ + refreshSessionToken?: boolean; +} /** * Pending MCP OAuth request ID and host-provided token or cancellation response. * @@ -9743,6 +9905,23 @@ export interface PermissionDecisionApproveForSessionApprovalExtensionManagement */ operation?: string; } +/** + * Session-scoped factory approval, optionally narrowed by approval key. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionApproveForSessionApprovalFactory". + */ +/** @experimental */ +export interface PermissionDecisionApproveForSessionApprovalFactory { + /** + * Approval covering factory operations. + */ + kind: "factory"; + /** + * Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + */ + approvalKey?: string; +} /** * Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. * @@ -9906,6 +10085,23 @@ export interface PermissionDecisionApproveForLocationApprovalExtensionManagement */ operation?: string; } +/** + * Location-scoped factory approval, optionally narrowed by approval key. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionApproveForLocationApprovalFactory". + */ +/** @experimental */ +export interface PermissionDecisionApproveForLocationApprovalFactory { + /** + * Approval covering factory operations. + */ + kind: "factory"; + /** + * Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + */ + approvalKey?: string; +} /** * Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. * @@ -10281,6 +10477,23 @@ export interface PermissionsLocationsAddToolApprovalDetailsExtensionManagement { */ operation?: string; } +/** + * Location-persisted factory approval, optionally narrowed by approval key. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionsLocationsAddToolApprovalDetailsFactory". + */ +/** @experimental */ +export interface PermissionsLocationsAddToolApprovalDetailsFactory { + /** + * Approval covering factory operations. + */ + kind: "factory"; + /** + * Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + */ + approvalKey?: string; +} /** * Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. * @@ -10785,7 +10998,7 @@ export interface PermissionsSetAllowAllRequest { */ enabled?: boolean; /** - * Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + * Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. */ model?: string; source?: PermissionsSetAllowAllSource; @@ -12812,6 +13025,10 @@ export interface SandboxConfig { * Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ ghAuth?: boolean; + /** + * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + */ + allowDevToolCaches?: boolean; } /** * User-managed sandbox policy fragment merged into the auto-discovered base policy. @@ -18444,6 +18661,30 @@ export function createServerRpc(connection: MessageConnection) { connection.sendRequest("mcp.discover", params), }, /** @experimental */ + extensions: { + /** + * Discovers user and enabled installed-plugin extensions from persisted Copilot home state, including enablement preferences. Launch-scoped additional plugins are not included. + * + * @returns Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + */ + discover: async (): Promise => + connection.sendRequest("extensions.discover", {}), + /** + * Persistently enables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.enable to update them. + * + * @param params Source-qualified extension identifiers to persistently enable for future sessions. + */ + enable: async (params: DiscoveredExtensionsEnableRequest): Promise => + connection.sendRequest("extensions.enable", params), + /** + * Persistently disables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.disable to update them. + * + * @param params Source-qualified extension identifiers to persistently disable for future sessions. + */ + disable: async (params: DiscoveredExtensionsDisableRequest): Promise => + connection.sendRequest("extensions.disable", params), + }, + /** @experimental */ plugins: { /** * Lists plugins installed in user/global state. @@ -19568,6 +19809,13 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ list: async (params?: SessionAgentListRequest): Promise => connection.sendRequest("session.agent.list", { sessionId, ...params }), + /** + * Sets an in-memory authored prompt override for an available agent. For built-in agents, this replaces only the static base prompt while preserving runtime-owned dynamic prompt composition and behavior. The special `general-purpose` agent is not overrideable. Overrides are not persisted; resumed and forked sessions start without them, so the host must re-apply them. + * + * @param params An in-memory authored prompt override for an available agent. + */ + setPrompt: async (params: AgentSetPromptRequest): Promise => + connection.sendRequest("session.agent.setPrompt", { sessionId, ...params }), /** * Gets the currently selected custom agent for the session. * @@ -19844,6 +20092,13 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ handlePendingRequest: async (params: McpOauthHandlePendingRequest): Promise => connection.sendRequest("session.mcp.oauth.handlePendingRequest", { sessionId, ...params }), + /** + * Notifies the session that MCP OAuth authentication succeeded and updated credentials were persisted, so cached tool definitions can be refreshed. + * + * @param params Identifies the MCP server whose persisted OAuth credentials were updated. + */ + authenticationStateChanged: async (params: McpOauthAuthenticationStateChangedRequest): Promise => + connection.sendRequest("session.mcp.oauth.authenticationStateChanged", { sessionId, ...params }), /** * Starts OAuth authentication for a remote MCP server. * @@ -20744,7 +20999,7 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin /** @experimental */ eventLog: { /** - * Reads a batch of session events from a cursor, optionally waiting for new events. + * Reads a batch of session events from a cursor, optionally waiting for new events. Supports tail-first reads via `direction: backward`. * * @param params Cursor, batch size, and optional long-poll/filter parameters for reading session events. * diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 4e5fb4274..e6d059ab4 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -509,6 +509,7 @@ export type SystemNotification = | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered + | SystemNotificationFactoryCompleted | SystemNotificationUnclassified; /** * Whether the agent completed successfully or failed @@ -518,6 +519,18 @@ export type SystemNotificationAgentCompletedStatus = | "completed" /** The agent failed. */ | "failed"; +/** + * Terminal status reached by a factory execution attempt. + */ +export type SystemNotificationFactoryCompletedStatus = + /** The factory completed successfully. */ + | "completed" + /** The factory was halted. */ + | "halted" + /** The factory was cancelled. */ + | "cancelled" + /** The factory failed. */ + | "error"; /** * Details of the permission being requested */ @@ -531,6 +544,7 @@ export type PermissionRequest = | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement + | PermissionRequestFactory | PermissionRequestExtensionPermissionAccess; /** * Whether this is a store or vote memory operation @@ -548,6 +562,14 @@ export type PermissionRequestMemoryDirection = | "upvote" /** Vote that the memory is incorrect or outdated. */ | "downvote"; +/** + * Operation gated by a factory permission request. + */ +export type FactoryPermissionOperation = + /** Running a registered factory, which spends subagents, active time, and AI credits under the approved limits. */ + | "run" + /** Authoring a factory, which writes JavaScript into a session-scoped extension and loads it. */ + | "author"; /** * Derived user-facing permission prompt details for UI consumers */ @@ -562,6 +584,7 @@ export type PermissionPromptRequest = | PermissionPromptRequestPath | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement + | PermissionPromptRequestFactory | PermissionPromptRequestExtensionPermissionAccess; /** * Why the auto-approval judge produced no usable recommendation. Present only alongside an `error` recommendation, where the human-readable reason is a fixed string and therefore cannot distinguish these cases. Intended to make a judge failure reportable by a consumer that has no access to the host's logs. @@ -625,6 +648,7 @@ export type UserToolSessionApproval = | UserToolSessionApprovalMemory | UserToolSessionApprovalCustomTool | UserToolSessionApprovalExtensionManagement + | UserToolSessionApprovalFactory | UserToolSessionApprovalExtensionPermissionAccess; /** * Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. @@ -894,6 +918,7 @@ export interface StartData { * When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. */ detachedFromSpawningParentSessionId?: string; + githubMcpToolConfig?: GitHubMcpToolConfig; /** * Identifier of the software producing the events (e.g., "copilot-agent") */ @@ -964,6 +989,27 @@ export interface WorkingDirectoryContext { */ repositoryHost?: string; } +/** + * Per-session configuration for the built-in GitHub MCP server + */ +export interface GitHubMcpToolConfig { + /** + * Additional GitHub MCP tools requested by the session + */ + additionalTools?: string[]; + /** + * Additional GitHub MCP toolsets requested by the session + */ + additionalToolsets?: string[]; + /** + * Whether to use the read-write endpoint and request all toolsets + */ + enableAllTools?: boolean; + /** + * Whether to request the GitHub MCP insiders build + */ + enableInsidersMode?: boolean; +} /** * Optional session limits. */ @@ -3566,6 +3612,14 @@ export interface AssistantMessageData { * Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. */ apiCallId?: string; + /** + * Total messages the model call's response was split into, one per reasoning boundary. Absent for a single-message response; the last chunk is the one where chunkIndex is chunkCount - 1. + */ + chunkCount?: number; + /** + * Zero-based position of this message within its model call's response. Absent when the response was not split into chunks. + */ + chunkIndex?: number; /** * Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. * @@ -6093,6 +6147,54 @@ export interface SystemNotificationInstructionDiscovered { */ type: "instruction_discovered"; } +/** + * System notification metadata for a factory execution attempt that reached a terminal state. + */ +export interface SystemNotificationFactoryCompleted { + /** + * Execution attempt that reached this terminal state. + */ + attempt: number; + /** + * Consumed AI usage in nano-AIU. + */ + consumedNanoAiu: number; + /** + * Subagents consumed by the run across all attempts. + */ + consumedSubagents: number; + /** + * Accumulated active execution time in milliseconds. + */ + elapsedMs: number; + /** + * Persisted factory name. + */ + factoryName: string; + /** + * Machine-readable terminal failure details, when present. + */ + failure?: { + [k: string]: unknown | undefined; + }; + /** + * Bounded prompt-safe preview of the completed result. + */ + resultPreview?: string; + /** + * Actionable run_factory resume guidance for a resource-limit failure. + */ + retryGuidance?: string; + /** + * Factory run identifier. + */ + runId: string; + status: SystemNotificationFactoryCompletedStatus; + /** + * Type discriminator. Always "factory_completed". + */ + type: "factory_completed"; +} /** * System notification metadata from an external host that does not match a runtime-owned notification kind. */ @@ -6511,6 +6613,73 @@ export interface PermissionRequestExtensionManagement { */ toolCallId?: string; } +/** + * Factory run or authoring permission request + */ +export interface PermissionRequestFactory { + /** + * Canonical key used for scoped factory approvals + */ + approvalKey: string; + /** + * Whether this factory is eligible for persistent approval + */ + canPersistApproval: boolean; + declaredMaxAiCredits?: number; + declaredMaxConcurrentSubagents?: number; + declaredMaxTotalSubagents?: number; + declaredTimeoutSeconds?: number; + /** + * Factory description + */ + description: string; + /** + * Permission kind discriminator + */ + kind: "factory"; + /** + * Effective AI-credit limit; omitted means unlimited + */ + maxAiCredits?: number; + /** + * Effective concurrent-subagent limit; omitted means unlimited + */ + maxConcurrentSubagents?: number; + /** + * Effective total-subagent limit; omitted means unlimited + */ + maxTotalSubagents?: number; + /** + * Factory name + */ + name: string; + operation: FactoryPermissionOperation; + /** + * Declared factory phases + */ + phases: FactoryPermissionPhase[]; + /** + * Effective active-time limit in seconds; omitted means unlimited + */ + timeoutSeconds?: number; + /** + * Tool call ID that triggered this permission request + */ + toolCallId?: string; +} +/** + * A declared phase shown in a factory permission prompt. + */ +export interface FactoryPermissionPhase { + /** + * Optional phase detail + */ + detail?: string; + /** + * Phase title + */ + title: string; +} /** * Extension permission access request */ @@ -6899,6 +7068,70 @@ export interface PermissionPromptRequestExtensionManagement { */ toolCallId?: string; } +/** + * Factory run or authoring permission prompt + */ +export interface PermissionPromptRequestFactory { + /** + * Canonical key used for scoped factory approvals + */ + approvalKey: string; + /** + * Auto-approval judge information for this request; present only when auto mode is enabled. + * + * @experimental + */ + autoApproval?: PermissionAutoApproval; + /** + * Whether this factory is eligible for persistent approval + */ + canPersistApproval: boolean; + declaredMaxAiCredits?: number; + declaredMaxConcurrentSubagents?: number; + declaredMaxTotalSubagents?: number; + declaredTimeoutSeconds?: number; + /** + * Factory description + */ + description: string; + /** + * Prompt kind discriminator + */ + kind: "factory"; + /** + * Whether managed policy requires a human response and forbids host auto-approval + */ + managedApprovalRequired?: boolean; + /** + * Effective AI-credit limit; omitted means unlimited + */ + maxAiCredits?: number; + /** + * Effective concurrent-subagent limit; omitted means unlimited + */ + maxConcurrentSubagents?: number; + /** + * Effective total-subagent limit; omitted means unlimited + */ + maxTotalSubagents?: number; + /** + * Factory name + */ + name: string; + operation: FactoryPermissionOperation; + /** + * Declared factory phases + */ + phases: FactoryPermissionPhase[]; + /** + * Effective active-time limit in seconds; omitted means unlimited + */ + timeoutSeconds?: number; + /** + * Tool call ID that triggered this permission request + */ + toolCallId?: string; +} /** * Extension permission access prompt */ @@ -7072,6 +7305,19 @@ export interface UserToolSessionApprovalExtensionManagement { */ operation?: string; } +/** + * Session-scoped factory approval, optionally narrowed by approval key. + */ +export interface UserToolSessionApprovalFactory { + /** + * Optional factory operation name or canonical approval key + */ + approvalKey?: string; + /** + * Factory approval kind + */ + kind: "factory"; +} /** * Session-scoped tool-approval rule for an extension's permission-gated capability access, keyed by extension name. */ diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 0d1d90fbb..e0a3df0e6 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -461,7 +461,7 @@ export class CopilotSession { }, }); - return toPublicFactoryRunResult(envelope); + return this.settleFactoryRun(envelope); }) as SessionFactoryApi["run"], resume: (async (runId: string, options?: Parameters[1]) => { let response; @@ -483,7 +483,7 @@ export class CopilotSession { } throw error; } - return toPublicFactoryRunResult(response.run); + return this.settleFactoryRun(response.run); }) as SessionFactoryApi["resume"], getRun: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.getRun({ runId })), waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal), @@ -494,6 +494,20 @@ export class CopilotSession { cancel: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.cancel({ runId })), }; + /** + * Resolve a start/resume envelope into the terminal envelope callers expect. + * + * The CLI may answer `session.factory.run` and `session.factory.resume` + * before the run settles, so a non-terminal envelope is followed by a wait + * on the run's terminal state. + */ + private settleFactoryRun(envelope: WireFactoryRunResult): Promise { + if (isFactoryRunTerminal(envelope.status)) { + return Promise.resolve(toPublicFactoryRunResult(envelope)); + } + return this.waitForFactoryRun(envelope.runId); + } + /** * Resolve when a factory run reaches a terminal status. * diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index b8c8e395a..fb9d58675 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -1809,6 +1809,84 @@ def to_dict(self) -> dict: result["path"] = from_union([from_str, from_none], self.path) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class DiscoveredExtensionPlugin: + """Containing plugin metadata for plugin-contributed extensions + + Installed plugin that contributes a discovered extension. + """ + name: str + """Installed plugin name""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredExtensionPlugin': + assert isinstance(obj, dict) + name = from_str(obj.get("name")) + return DiscoveredExtensionPlugin(name) + + def to_dict(self) -> dict: + result: dict = {} + result["name"] = from_str(self.name) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +class DiscoveredExtensionSource(Enum): + """Discovery source + + Persisted extension discovery source + """ + PLUGIN = "plugin" + USER = "user" + +# Experimental: this type is part of an experimental API and may change or be removed. +class DiscoveredExtensionMode(Enum): + """Effective extension loading and agent-management mode + + Effective extension loading mode. Defaults to load_and_augment when unset. + """ + DISABLED = "disabled" + LOAD_AND_AUGMENT = "load_and_augment" + LOAD_ONLY = "load_only" + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class DiscoveredExtensionsDisableRequest: + """Source-qualified extension identifiers to persistently disable for future sessions.""" + + ids: list[str] + """Source-qualified user or plugin extension IDs to disable""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredExtensionsDisableRequest': + assert isinstance(obj, dict) + ids = from_list(from_str, obj.get("ids")) + return DiscoveredExtensionsDisableRequest(ids) + + def to_dict(self) -> dict: + result: dict = {} + result["ids"] = from_list(from_str, self.ids) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class DiscoveredExtensionsEnableRequest: + """Source-qualified extension identifiers to persistently enable for future sessions.""" + + ids: list[str] + """Source-qualified user or plugin extension IDs to enable""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredExtensionsEnableRequest': + assert isinstance(obj, dict) + ids = from_list(from_str, obj.get("ids")) + return DiscoveredExtensionsEnableRequest(ids) + + def to_dict(self) -> dict: + result: dict = {} + result["ids"] = from_list(from_str, self.ids) + return result + # Experimental: this type is part of an experimental API and may change or be removed. class DiscoveredMCPServerType(Enum): """Server transport type: stdio, http, sse (deprecated), or memory""" @@ -1873,6 +1951,28 @@ class EventsAgentScope(Enum): ALL = "all" PRIMARY = "primary" +# Experimental: this type is part of an experimental API and may change or be removed. +class EventsReadDirection(Enum): + """Direction to page through the session's persisted event history. 'forward' (default) + pages from the cursor toward newer events (or from the start of history when no cursor is + given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` + events, and the returned cursor pages toward OLDER events on subsequent backward reads. + Events within a returned batch are always in chronological (oldest-to-newest) order, even + for a backward read. Backward reads cover PERSISTED history only; ephemeral events are + never returned by a backward read. `direction` selects the INITIAL read only: the + returned cursor is self-describing, so a continuation read pages in the cursor's own + direction regardless of the `direction` passed alongside it — a forward cursor always + pages forward and a backward cursor always pages backward. Pass the direction that + matches the cursor to avoid confusion. + + Direction to page through the session's persisted event history. 'forward' pages from the + cursor toward newer events; 'backward' returns the newest window first (tail-first) and + pages toward older events. Events within a returned batch are always chronological + (oldest-to-newest), even for a backward read. + """ + BACKWARD = "backward" + FORWARD = "forward" + # Experimental: this type is part of an experimental API and may change or be removed. class EventLogTypes(Enum): EMPTY = "*" @@ -1926,7 +2026,22 @@ def to_dict(self) -> dict: class EventsCursorStatus(Enum): """Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) - and the read started from the beginning of the remaining history. + and the read fell back to a boundary of the remaining history (the beginning for a + forward read, the tail for a backward read). The fallback page is a fresh boundary + snapshot, not a continuation of the requested cursor, so it may overlap already-rendered + events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate + by event id) before continuing from the returned cursor. + + Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor + referred to an event that no longer exists in history (e.g. truncated or compacted away) + and the read fell back to a boundary of the remaining history. For a forward read the + fallback starts from the beginning of the remaining history; for a backward read it falls + back to the tail (the newest window). Because the fallback page is a fresh boundary + snapshot rather than a continuation of the requested cursor, it may overlap events the + consumer has already rendered — a backward fallback to the tail in particular can repeat + the newest window. On 'expired', consumers should reset or rebase their local pagination + state (or deduplicate by event id) before continuing from the returned cursor rather than + blindly appending/prepending the fallback page. """ EXPIRED = "expired" OK = "ok" @@ -4415,6 +4530,35 @@ class MCPToolUIVisibility(Enum): APP = "app" MODEL = "model" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class MCPOauthAuthenticationStateChangedRequest: + """Identifies the MCP server whose persisted OAuth credentials were updated.""" + + refresh_session_token: bool | None = None + """Whether the target session must mint a session-scoped access token instead of reusing a + shared access token persisted by another session. + """ + server_name: str | None = None + """Name of the MCP server whose OAuth credentials were updated. Omit only when the host + cannot identify the server. + """ + + @staticmethod + def from_dict(obj: Any) -> 'MCPOauthAuthenticationStateChangedRequest': + assert isinstance(obj, dict) + refresh_session_token = from_union([from_bool, from_none], obj.get("refreshSessionToken")) + server_name = from_union([from_str, from_none], obj.get("serverName")) + return MCPOauthAuthenticationStateChangedRequest(refresh_session_token, server_name) + + def to_dict(self) -> dict: + result: dict = {} + if self.refresh_session_token is not None: + result["refreshSessionToken"] = from_union([from_bool, from_none], self.refresh_session_token) + if self.server_name is not None: + result["serverName"] = from_union([from_str, from_none], self.server_name) + return result + class MCPOauthPendingRequestResponseKind(Enum): CANCELLED = "cancelled" TOKEN = "token" @@ -5595,6 +5739,7 @@ class ApprovalKind(Enum): CUSTOM_TOOL = "custom-tool" EXTENSION_MANAGEMENT = "extension-management" EXTENSION_PERMISSION_ACCESS = "extension-permission-access" + FACTORY = "factory" MCP = "mcp" MCP_SAMPLING = "mcp-sampling" MEMORY = "memory" @@ -5633,6 +5778,9 @@ class PermissionDecisionApproveForLocationApprovalExtensionManagementKind(Enum): class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind(Enum): EXTENSION_PERMISSION_ACCESS = "extension-permission-access" +class PermissionDecisionApproveForLocationApprovalFactoryKind(Enum): + FACTORY = "factory" + class PermissionDecisionApproveForLocationApprovalMCPKind(Enum): MCP = "mcp" @@ -12999,11 +13147,62 @@ def to_dict(self) -> dict: result["outputDirectory"] = from_union([from_str, from_none], self.output_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class DiscoveredExtension: + """Discovered extension metadata and persistent enablement state.""" + + enabled: bool + """Whether this extension's persistent per-ID preference is enabled""" + + id: str + """Source-qualified ID accepted by both server and session extension enablement methods""" + + name: str + """Human-readable extension name""" + + path: str + """Absolute path to the extension entry module, suitable for revealing it in a file manager""" + + source: DiscoveredExtensionSource + """Discovery source""" + + plugin: DiscoveredExtensionPlugin | None = None + """Containing plugin metadata for plugin-contributed extensions""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredExtension': + assert isinstance(obj, dict) + enabled = from_bool(obj.get("enabled")) + id = from_str(obj.get("id")) + name = from_str(obj.get("name")) + path = from_str(obj.get("path")) + source = DiscoveredExtensionSource(obj.get("source")) + plugin = from_union([DiscoveredExtensionPlugin.from_dict, from_none], obj.get("plugin")) + return DiscoveredExtension(enabled, id, name, path, source, plugin) + + def to_dict(self) -> dict: + result: dict = {} + result["enabled"] = from_bool(self.enabled) + result["id"] = from_str(self.id) + result["name"] = from_str(self.name) + result["path"] = from_str(self.path) + result["source"] = to_enum(DiscoveredExtensionSource, self.source) + if self.plugin is not None: + result["plugin"] = from_union([lambda x: to_class(DiscoveredExtensionPlugin, x), from_none], self.plugin) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class EventLogReadRequest: """Cursor, batch size, and optional long-poll/filter parameters for reading session events.""" + agent_ids: list[str] | None = None + """Optional non-empty list of subagent identifiers. When provided, only events owned by one + of these agents are returned; ownership recognizes the event envelope's agentId plus + legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over + agentScope. + """ agent_scope: EventsAgentScope | None = None """Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns @@ -13014,11 +13213,25 @@ class EventLogReadRequest: """Opaque cursor returned by a previous read. Omit on the first call to start from the beginning of the session's persisted history. """ + direction: EventsReadDirection | None = None + """Direction to page through the session's persisted event history. 'forward' (default) + pages from the cursor toward newer events (or from the start of history when no cursor is + given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` + events, and the returned cursor pages toward OLDER events on subsequent backward reads. + Events within a returned batch are always in chronological (oldest-to-newest) order, even + for a backward read. Backward reads cover PERSISTED history only; ephemeral events are + never returned by a backward read. `direction` selects the INITIAL read only: the + returned cursor is self-describing, so a continuation read pages in the cursor's own + direction regardless of the `direction` passed alongside it — a forward cursor always + pages forward and a backward cursor always pages backward. Pass the direction that + matches the cursor to avoid confusion. + """ include_ephemeral: bool | None = None """When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). + Ignored by backward reads, which always cover persisted history only. """ max: int | None = None """Maximum number of events to return in this batch (1–1000, default 200).""" @@ -13031,26 +13244,35 @@ class EventLogReadRequest: (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture - future ephemerals as they happen). + future ephemerals as they happen). This applies to forward reads only: a backward read + always returns immediately and ignores `waitMs`, because backward paging covers persisted + history only while new events append at the tail (the opposite end from a backward page), + so no blocking or ephemeral delivery can occur. """ @staticmethod def from_dict(obj: Any) -> 'EventLogReadRequest': assert isinstance(obj, dict) + agent_ids = from_union([lambda x: from_list(from_str, x), from_none], obj.get("agentIds")) agent_scope = from_union([EventsAgentScope, from_none], obj.get("agentScope")) cursor = from_union([from_str, from_none], obj.get("cursor")) + direction = from_union([EventsReadDirection, from_none], obj.get("direction")) include_ephemeral = from_union([from_bool, from_none], obj.get("includeEphemeral")) max = from_union([from_int, from_none], obj.get("max")) types = from_union([lambda x: from_list(from_str, x), EventLogTypes, from_none], obj.get("types")) wait_ms = from_union([from_int, from_none], obj.get("waitMs")) - return EventLogReadRequest(agent_scope, cursor, include_ephemeral, max, types, wait_ms) + return EventLogReadRequest(agent_ids, agent_scope, cursor, direction, include_ephemeral, max, types, wait_ms) def to_dict(self) -> dict: result: dict = {} + if self.agent_ids is not None: + result["agentIds"] = from_union([lambda x: from_list(from_str, x), from_none], self.agent_ids) if self.agent_scope is not None: result["agentScope"] = from_union([lambda x: to_enum(EventsAgentScope, x), from_none], self.agent_scope) if self.cursor is not None: result["cursor"] = from_union([from_str, from_none], self.cursor) + if self.direction is not None: + result["direction"] = from_union([lambda x: to_enum(EventsReadDirection, x), from_none], self.direction) if self.include_ephemeral is not None: result["includeEphemeral"] = from_union([from_bool, from_none], self.include_ephemeral) if self.max is not None: @@ -13068,24 +13290,34 @@ class EventsReadResult: cursor: str """Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue - from where this read left off. Always present, even when no events were returned. + from where this read left off. Always present, even when no events were returned. For a + backward read this cursor pages toward OLDER events; keep passing `direction: backward` + with it (the cursor is also self-describing, so backward paging continues correctly). """ cursor_status: EventsCursorStatus """Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) - and the read started from the beginning of the remaining history. + and the read fell back to a boundary of the remaining history. For a forward read the + fallback starts from the beginning of the remaining history; for a backward read it falls + back to the tail (the newest window). Because the fallback page is a fresh boundary + snapshot rather than a continuation of the requested cursor, it may overlap events the + consumer has already rendered — a backward fallback to the tail in particular can repeat + the newest window. On 'expired', consumers should reset or rebase their local pagination + state (or deduplicate by event id) before continuing from the returned cursor rather than + blindly appending/prepending the fallback page. """ events: list[SessionEvent] """Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep - reading with a non-zero `waitMs`. + reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window + contains persisted events only, still in chronological (oldest-to-newest) append order. """ has_more: bool - """True when the read returned `max` events and more events are available immediately. When - false, the next read with a non-zero `waitMs` will block until a new event arrives or the - wait expires. + """True when more events are available in the read's direction. For a forward read, true + means the batch returned `max` events and more are available immediately. For a backward + read, true means older persisted events remain before the returned window. """ @staticmethod @@ -16371,6 +16603,84 @@ def to_dict(self) -> dict: result["operation"] = from_union([from_str, from_none], self.operation) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionApproveForLocationApprovalFactory: + """Location-scoped factory approval, optionally narrowed by approval key.""" + + kind: ClassVar[str] = "factory" + """Approval covering factory operations.""" + + approval_key: str | None = None + """Optional factory operation name or canonical approval key; when omitted, the approval + covers all factory operations. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalFactory': + assert isinstance(obj, dict) + approval_key = from_union([from_str, from_none], obj.get("approvalKey")) + return PermissionDecisionApproveForLocationApprovalFactory(approval_key) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.approval_key is not None: + result["approvalKey"] = from_union([from_str, from_none], self.approval_key) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionApproveForSessionApprovalFactory: + """Session-scoped factory approval, optionally narrowed by approval key.""" + + kind: ClassVar[str] = "factory" + """Approval covering factory operations.""" + + approval_key: str | None = None + """Optional factory operation name or canonical approval key; when omitted, the approval + covers all factory operations. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalFactory': + assert isinstance(obj, dict) + approval_key = from_union([from_str, from_none], obj.get("approvalKey")) + return PermissionDecisionApproveForSessionApprovalFactory(approval_key) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.approval_key is not None: + result["approvalKey"] = from_union([from_str, from_none], self.approval_key) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionsLocationsAddToolApprovalDetailsFactory: + """Location-persisted factory approval, optionally narrowed by approval key.""" + + kind: ClassVar[str] = "factory" + """Approval covering factory operations.""" + + approval_key: str | None = None + """Optional factory operation name or canonical approval key; when omitted, the approval + covers all factory operations. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsFactory': + assert isinstance(obj, dict) + approval_key = from_union([from_str, from_none], obj.get("approvalKey")) + return PermissionsLocationsAddToolApprovalDetailsFactory(approval_key) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.approval_key is not None: + result["approvalKey"] = from_union([from_str, from_none], self.approval_key) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionDecisionApproveForLocationApprovalMCP: @@ -17229,6 +17539,30 @@ def to_dict(self) -> dict: result["rows"] = from_list(lambda x: to_class(PlanSQLTodosRow, x), self.rows) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class AgentSetPromptRequest: + """An in-memory authored prompt override for an available agent.""" + + id: str + """Stable effective agent id. Plugin namespace separators are normalized.""" + + prompt: str + """Replacement authored prompt. Empty text is valid.""" + + @staticmethod + def from_dict(obj: Any) -> 'AgentSetPromptRequest': + assert isinstance(obj, dict) + id = from_str(obj.get("id")) + prompt = from_str(obj.get("prompt")) + return AgentSetPromptRequest(id, prompt) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = from_str(self.id) + result["prompt"] = from_str(self.prompt) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredMCPServer: @@ -21051,6 +21385,31 @@ def to_dict(self) -> dict: result["skippedEntries"] = from_union([lambda x: from_list(lambda x: to_class(DebugCollectLogsSkippedEntry, x), x), from_none], self.skipped_entries) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class DiscoveredExtensions: + """Extensions discovered from persisted Copilot home state and their effective loading mode. + Launch-scoped additional plugins are not included. + """ + extensions: list[DiscoveredExtension] + """Discovered user and enabled installed-plugin extensions from persisted Copilot home state""" + + mode: DiscoveredExtensionMode + """Effective extension loading mode. Defaults to load_and_augment when unset.""" + + @staticmethod + def from_dict(obj: Any) -> 'DiscoveredExtensions': + assert isinstance(obj, dict) + extensions = from_list(DiscoveredExtension.from_dict, obj.get("extensions")) + mode = DiscoveredExtensionMode(obj.get("mode")) + return DiscoveredExtensions(extensions, mode) + + def to_dict(self) -> dict: + result: dict = {} + result["extensions"] = from_list(lambda x: to_class(DiscoveredExtension, x), self.extensions) + result["mode"] = to_enum(DiscoveredExtensionMode, self.mode) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExtensionList: @@ -24843,6 +25202,14 @@ class SandboxConfig: add_current_working_directory: bool | None = None """Whether to auto-add the current working directory to readwritePaths. Default: true.""" + allow_dev_tool_caches: bool | None = None + """Whether to auto-grant read access to common developer-tool caches, registries, and + toolchains in their default home locations (cargo, go, npm, Maven, and more), plus + read-write access to (and, on Unix, up-front creation of) the scratch caches builds write + on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so + builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; + set to false to opt out). + """ gh_auth: bool | None = None """Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). @@ -24860,16 +25227,19 @@ def from_dict(obj: Any) -> 'SandboxConfig': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) add_current_working_directory = from_union([from_bool, from_none], obj.get("addCurrentWorkingDirectory")) + allow_dev_tool_caches = from_union([from_bool, from_none], obj.get("allowDevToolCaches")) gh_auth = from_union([from_bool, from_none], obj.get("ghAuth")) git_auth = from_union([from_bool, from_none], obj.get("gitAuth")) user_policy = from_union([SandboxConfigUserPolicy.from_dict, from_none], obj.get("userPolicy")) - return SandboxConfig(enabled, add_current_working_directory, gh_auth, git_auth, user_policy) + return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_caches, gh_auth, git_auth, user_policy) def to_dict(self) -> dict: result: dict = {} result["enabled"] = from_bool(self.enabled) if self.add_current_working_directory is not None: result["addCurrentWorkingDirectory"] = from_union([from_bool, from_none], self.add_current_working_directory) + if self.allow_dev_tool_caches is not None: + result["allowDevToolCaches"] = from_union([from_bool, from_none], self.allow_dev_tool_caches) if self.gh_auth is not None: result["ghAuth"] = from_union([from_bool, from_none], self.gh_auth) if self.git_auth is not None: @@ -27381,7 +27751,8 @@ class PermissionsSetAllowAllRequest: """ model: str | None = None """Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when - `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge + model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. """ source: PermissionsSetAAllSource | None = None """Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers.""" @@ -27944,6 +28315,7 @@ class RPC: agents_discover_request: AgentsDiscoverRequest agent_select_request: AgentSelectRequest agent_select_result: AgentSelectResult + agent_set_prompt_request: AgentSetPromptRequest agents_get_discovery_paths_request: AgentsGetDiscoveryPathsRequest allow_all_permission_set_result: AllowAllPermissionSetResult allow_all_permission_state: AllowAllPermissionState @@ -28012,6 +28384,13 @@ class RPC: debug_collect_logs_skipped_entry: DebugCollectLogsSkippedEntry debug_collect_logs_source: DebugCollectLogsSource discovered_canvas: DiscoveredCanvas + discovered_extension: DiscoveredExtension + discovered_extension_mode: DiscoveredExtensionMode + discovered_extension_plugin: DiscoveredExtensionPlugin + discovered_extensions: DiscoveredExtensions + discovered_extensions_disable_request: DiscoveredExtensionsDisableRequest + discovered_extensions_enable_request: DiscoveredExtensionsEnableRequest + discovered_extension_source: DiscoveredExtensionSource discovered_mcp_server: DiscoveredMCPServer discovered_mcp_server_type: DiscoveredMCPServerType enqueue_command_params: EnqueueCommandParams @@ -28023,6 +28402,7 @@ class RPC: event_log_types: list[str] | EventLogTypes events_agent_scope: EventsAgentScope events_cursor_status: EventsCursorStatus + events_read_direction: EventsReadDirection events_read_result: EventsReadResult execute_command_params: ExecuteCommandParams execute_command_result: ExecuteCommandResult @@ -28214,6 +28594,7 @@ class RPC: mcp_is_server_running_result: MCPIsServerRunningResult mcp_list_tools_request: MCPListToolsRequest mcp_list_tools_result: MCPListToolsResult + mcp_oauth_authentication_state_changed_request: MCPOauthAuthenticationStateChangedRequest mcp_oauth_handle_pending_request: MCPOauthHandlePendingRequest mcp_oauth_handle_pending_result: MCPOauthHandlePendingResult mcp_oauth_login_grant_type: MCPGrantType @@ -28329,6 +28710,7 @@ class RPC: permission_decision_approve_for_location_approval_custom_tool: PermissionDecisionApproveForLocationApprovalCustomTool permission_decision_approve_for_location_approval_extension_management: PermissionDecisionApproveForLocationApprovalExtensionManagement permission_decision_approve_for_location_approval_extension_permission_access: PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess + permission_decision_approve_for_location_approval_factory: PermissionDecisionApproveForLocationApprovalFactory permission_decision_approve_for_location_approval_mcp: PermissionDecisionApproveForLocationApprovalMCP permission_decision_approve_for_location_approval_mcp_sampling: PermissionDecisionApproveForLocationApprovalMCPSampling permission_decision_approve_for_location_approval_memory: PermissionDecisionApproveForLocationApprovalMemory @@ -28340,6 +28722,7 @@ class RPC: permission_decision_approve_for_session_approval_custom_tool: PermissionDecisionApproveForSessionApprovalCustomTool permission_decision_approve_for_session_approval_extension_management: PermissionDecisionApproveForSessionApprovalExtensionManagement permission_decision_approve_for_session_approval_extension_permission_access: PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess + permission_decision_approve_for_session_approval_factory: PermissionDecisionApproveForSessionApprovalFactory permission_decision_approve_for_session_approval_mcp: PermissionDecisionApproveForSessionApprovalMCP permission_decision_approve_for_session_approval_mcp_sampling: PermissionDecisionApproveForSessionApprovalMCPSampling permission_decision_approve_for_session_approval_memory: PermissionDecisionApproveForSessionApprovalMemory @@ -28387,6 +28770,7 @@ class RPC: permissions_locations_add_tool_approval_details_custom_tool: PermissionsLocationsAddToolApprovalDetailsCustomTool permissions_locations_add_tool_approval_details_extension_management: PermissionsLocationsAddToolApprovalDetailsExtensionManagement permissions_locations_add_tool_approval_details_extension_permission_access: PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess + permissions_locations_add_tool_approval_details_factory: PermissionsLocationsAddToolApprovalDetailsFactory permissions_locations_add_tool_approval_details_mcp: PermissionsLocationsAddToolApprovalDetailsMCP permissions_locations_add_tool_approval_details_mcp_sampling: PermissionsLocationsAddToolApprovalDetailsMCPSampling permissions_locations_add_tool_approval_details_memory: PermissionsLocationsAddToolApprovalDetailsMemory @@ -28930,6 +29314,7 @@ def from_dict(obj: Any) -> 'RPC': agents_discover_request = AgentsDiscoverRequest.from_dict(obj.get("AgentsDiscoverRequest")) agent_select_request = AgentSelectRequest.from_dict(obj.get("AgentSelectRequest")) agent_select_result = AgentSelectResult.from_dict(obj.get("AgentSelectResult")) + agent_set_prompt_request = AgentSetPromptRequest.from_dict(obj.get("AgentSetPromptRequest")) agents_get_discovery_paths_request = AgentsGetDiscoveryPathsRequest.from_dict(obj.get("AgentsGetDiscoveryPathsRequest")) allow_all_permission_set_result = AllowAllPermissionSetResult.from_dict(obj.get("AllowAllPermissionSetResult")) allow_all_permission_state = AllowAllPermissionState.from_dict(obj.get("AllowAllPermissionState")) @@ -28998,6 +29383,13 @@ def from_dict(obj: Any) -> 'RPC': debug_collect_logs_skipped_entry = DebugCollectLogsSkippedEntry.from_dict(obj.get("DebugCollectLogsSkippedEntry")) debug_collect_logs_source = DebugCollectLogsSource(obj.get("DebugCollectLogsSource")) discovered_canvas = DiscoveredCanvas.from_dict(obj.get("DiscoveredCanvas")) + discovered_extension = DiscoveredExtension.from_dict(obj.get("DiscoveredExtension")) + discovered_extension_mode = DiscoveredExtensionMode(obj.get("DiscoveredExtensionMode")) + discovered_extension_plugin = DiscoveredExtensionPlugin.from_dict(obj.get("DiscoveredExtensionPlugin")) + discovered_extensions = DiscoveredExtensions.from_dict(obj.get("DiscoveredExtensions")) + discovered_extensions_disable_request = DiscoveredExtensionsDisableRequest.from_dict(obj.get("DiscoveredExtensionsDisableRequest")) + discovered_extensions_enable_request = DiscoveredExtensionsEnableRequest.from_dict(obj.get("DiscoveredExtensionsEnableRequest")) + discovered_extension_source = DiscoveredExtensionSource(obj.get("DiscoveredExtensionSource")) discovered_mcp_server = DiscoveredMCPServer.from_dict(obj.get("DiscoveredMcpServer")) discovered_mcp_server_type = DiscoveredMCPServerType(obj.get("DiscoveredMcpServerType")) enqueue_command_params = EnqueueCommandParams.from_dict(obj.get("EnqueueCommandParams")) @@ -29009,6 +29401,7 @@ def from_dict(obj: Any) -> 'RPC': event_log_types = from_union([lambda x: from_list(from_str, x), EventLogTypes], obj.get("EventLogTypes")) events_agent_scope = EventsAgentScope(obj.get("EventsAgentScope")) events_cursor_status = EventsCursorStatus(obj.get("EventsCursorStatus")) + events_read_direction = EventsReadDirection(obj.get("EventsReadDirection")) events_read_result = EventsReadResult.from_dict(obj.get("EventsReadResult")) execute_command_params = ExecuteCommandParams.from_dict(obj.get("ExecuteCommandParams")) execute_command_result = ExecuteCommandResult.from_dict(obj.get("ExecuteCommandResult")) @@ -29200,6 +29593,7 @@ def from_dict(obj: Any) -> 'RPC': mcp_is_server_running_result = MCPIsServerRunningResult.from_dict(obj.get("McpIsServerRunningResult")) mcp_list_tools_request = MCPListToolsRequest.from_dict(obj.get("McpListToolsRequest")) mcp_list_tools_result = MCPListToolsResult.from_dict(obj.get("McpListToolsResult")) + mcp_oauth_authentication_state_changed_request = MCPOauthAuthenticationStateChangedRequest.from_dict(obj.get("McpOauthAuthenticationStateChangedRequest")) mcp_oauth_handle_pending_request = MCPOauthHandlePendingRequest.from_dict(obj.get("McpOauthHandlePendingRequest")) mcp_oauth_handle_pending_result = MCPOauthHandlePendingResult.from_dict(obj.get("McpOauthHandlePendingResult")) mcp_oauth_login_grant_type = MCPGrantType(obj.get("McpOauthLoginGrantType")) @@ -29315,6 +29709,7 @@ def from_dict(obj: Any) -> 'RPC': permission_decision_approve_for_location_approval_custom_tool = PermissionDecisionApproveForLocationApprovalCustomTool.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalCustomTool")) permission_decision_approve_for_location_approval_extension_management = PermissionDecisionApproveForLocationApprovalExtensionManagement.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalExtensionManagement")) permission_decision_approve_for_location_approval_extension_permission_access = PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess")) + permission_decision_approve_for_location_approval_factory = PermissionDecisionApproveForLocationApprovalFactory.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalFactory")) permission_decision_approve_for_location_approval_mcp = PermissionDecisionApproveForLocationApprovalMCP.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalMcp")) permission_decision_approve_for_location_approval_mcp_sampling = PermissionDecisionApproveForLocationApprovalMCPSampling.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalMcpSampling")) permission_decision_approve_for_location_approval_memory = PermissionDecisionApproveForLocationApprovalMemory.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalMemory")) @@ -29326,6 +29721,7 @@ def from_dict(obj: Any) -> 'RPC': permission_decision_approve_for_session_approval_custom_tool = PermissionDecisionApproveForSessionApprovalCustomTool.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalCustomTool")) permission_decision_approve_for_session_approval_extension_management = PermissionDecisionApproveForSessionApprovalExtensionManagement.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalExtensionManagement")) permission_decision_approve_for_session_approval_extension_permission_access = PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess")) + permission_decision_approve_for_session_approval_factory = PermissionDecisionApproveForSessionApprovalFactory.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalFactory")) permission_decision_approve_for_session_approval_mcp = PermissionDecisionApproveForSessionApprovalMCP.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalMcp")) permission_decision_approve_for_session_approval_mcp_sampling = PermissionDecisionApproveForSessionApprovalMCPSampling.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalMcpSampling")) permission_decision_approve_for_session_approval_memory = PermissionDecisionApproveForSessionApprovalMemory.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalMemory")) @@ -29373,6 +29769,7 @@ def from_dict(obj: Any) -> 'RPC': permissions_locations_add_tool_approval_details_custom_tool = PermissionsLocationsAddToolApprovalDetailsCustomTool.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsCustomTool")) permissions_locations_add_tool_approval_details_extension_management = PermissionsLocationsAddToolApprovalDetailsExtensionManagement.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsExtensionManagement")) permissions_locations_add_tool_approval_details_extension_permission_access = PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess")) + permissions_locations_add_tool_approval_details_factory = PermissionsLocationsAddToolApprovalDetailsFactory.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsFactory")) permissions_locations_add_tool_approval_details_mcp = PermissionsLocationsAddToolApprovalDetailsMCP.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsMcp")) permissions_locations_add_tool_approval_details_mcp_sampling = PermissionsLocationsAddToolApprovalDetailsMCPSampling.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsMcpSampling")) permissions_locations_add_tool_approval_details_memory = PermissionsLocationsAddToolApprovalDetailsMemory.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsMemory")) @@ -29871,7 +30268,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -29916,6 +30313,7 @@ def to_dict(self) -> dict: result["AgentsDiscoverRequest"] = to_class(AgentsDiscoverRequest, self.agents_discover_request) result["AgentSelectRequest"] = to_class(AgentSelectRequest, self.agent_select_request) result["AgentSelectResult"] = to_class(AgentSelectResult, self.agent_select_result) + result["AgentSetPromptRequest"] = to_class(AgentSetPromptRequest, self.agent_set_prompt_request) result["AgentsGetDiscoveryPathsRequest"] = to_class(AgentsGetDiscoveryPathsRequest, self.agents_get_discovery_paths_request) result["AllowAllPermissionSetResult"] = to_class(AllowAllPermissionSetResult, self.allow_all_permission_set_result) result["AllowAllPermissionState"] = to_class(AllowAllPermissionState, self.allow_all_permission_state) @@ -29984,6 +30382,13 @@ def to_dict(self) -> dict: result["DebugCollectLogsSkippedEntry"] = to_class(DebugCollectLogsSkippedEntry, self.debug_collect_logs_skipped_entry) result["DebugCollectLogsSource"] = to_enum(DebugCollectLogsSource, self.debug_collect_logs_source) result["DiscoveredCanvas"] = to_class(DiscoveredCanvas, self.discovered_canvas) + result["DiscoveredExtension"] = to_class(DiscoveredExtension, self.discovered_extension) + result["DiscoveredExtensionMode"] = to_enum(DiscoveredExtensionMode, self.discovered_extension_mode) + result["DiscoveredExtensionPlugin"] = to_class(DiscoveredExtensionPlugin, self.discovered_extension_plugin) + result["DiscoveredExtensions"] = to_class(DiscoveredExtensions, self.discovered_extensions) + result["DiscoveredExtensionsDisableRequest"] = to_class(DiscoveredExtensionsDisableRequest, self.discovered_extensions_disable_request) + result["DiscoveredExtensionsEnableRequest"] = to_class(DiscoveredExtensionsEnableRequest, self.discovered_extensions_enable_request) + result["DiscoveredExtensionSource"] = to_enum(DiscoveredExtensionSource, self.discovered_extension_source) result["DiscoveredMcpServer"] = to_class(DiscoveredMCPServer, self.discovered_mcp_server) result["DiscoveredMcpServerType"] = to_enum(DiscoveredMCPServerType, self.discovered_mcp_server_type) result["EnqueueCommandParams"] = to_class(EnqueueCommandParams, self.enqueue_command_params) @@ -29995,6 +30400,7 @@ def to_dict(self) -> dict: result["EventLogTypes"] = from_union([lambda x: from_list(from_str, x), lambda x: to_enum(EventLogTypes, x)], self.event_log_types) result["EventsAgentScope"] = to_enum(EventsAgentScope, self.events_agent_scope) result["EventsCursorStatus"] = to_enum(EventsCursorStatus, self.events_cursor_status) + result["EventsReadDirection"] = to_enum(EventsReadDirection, self.events_read_direction) result["EventsReadResult"] = to_class(EventsReadResult, self.events_read_result) result["ExecuteCommandParams"] = to_class(ExecuteCommandParams, self.execute_command_params) result["ExecuteCommandResult"] = to_class(ExecuteCommandResult, self.execute_command_result) @@ -30186,6 +30592,7 @@ def to_dict(self) -> dict: result["McpIsServerRunningResult"] = to_class(MCPIsServerRunningResult, self.mcp_is_server_running_result) result["McpListToolsRequest"] = to_class(MCPListToolsRequest, self.mcp_list_tools_request) result["McpListToolsResult"] = to_class(MCPListToolsResult, self.mcp_list_tools_result) + result["McpOauthAuthenticationStateChangedRequest"] = to_class(MCPOauthAuthenticationStateChangedRequest, self.mcp_oauth_authentication_state_changed_request) result["McpOauthHandlePendingRequest"] = to_class(MCPOauthHandlePendingRequest, self.mcp_oauth_handle_pending_request) result["McpOauthHandlePendingResult"] = to_class(MCPOauthHandlePendingResult, self.mcp_oauth_handle_pending_result) result["McpOauthLoginGrantType"] = to_enum(MCPGrantType, self.mcp_oauth_login_grant_type) @@ -30301,6 +30708,7 @@ def to_dict(self) -> dict: result["PermissionDecisionApproveForLocationApprovalCustomTool"] = to_class(PermissionDecisionApproveForLocationApprovalCustomTool, self.permission_decision_approve_for_location_approval_custom_tool) result["PermissionDecisionApproveForLocationApprovalExtensionManagement"] = to_class(PermissionDecisionApproveForLocationApprovalExtensionManagement, self.permission_decision_approve_for_location_approval_extension_management) result["PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess"] = to_class(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess, self.permission_decision_approve_for_location_approval_extension_permission_access) + result["PermissionDecisionApproveForLocationApprovalFactory"] = to_class(PermissionDecisionApproveForLocationApprovalFactory, self.permission_decision_approve_for_location_approval_factory) result["PermissionDecisionApproveForLocationApprovalMcp"] = to_class(PermissionDecisionApproveForLocationApprovalMCP, self.permission_decision_approve_for_location_approval_mcp) result["PermissionDecisionApproveForLocationApprovalMcpSampling"] = to_class(PermissionDecisionApproveForLocationApprovalMCPSampling, self.permission_decision_approve_for_location_approval_mcp_sampling) result["PermissionDecisionApproveForLocationApprovalMemory"] = to_class(PermissionDecisionApproveForLocationApprovalMemory, self.permission_decision_approve_for_location_approval_memory) @@ -30312,6 +30720,7 @@ def to_dict(self) -> dict: result["PermissionDecisionApproveForSessionApprovalCustomTool"] = to_class(PermissionDecisionApproveForSessionApprovalCustomTool, self.permission_decision_approve_for_session_approval_custom_tool) result["PermissionDecisionApproveForSessionApprovalExtensionManagement"] = to_class(PermissionDecisionApproveForSessionApprovalExtensionManagement, self.permission_decision_approve_for_session_approval_extension_management) result["PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess"] = to_class(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess, self.permission_decision_approve_for_session_approval_extension_permission_access) + result["PermissionDecisionApproveForSessionApprovalFactory"] = to_class(PermissionDecisionApproveForSessionApprovalFactory, self.permission_decision_approve_for_session_approval_factory) result["PermissionDecisionApproveForSessionApprovalMcp"] = to_class(PermissionDecisionApproveForSessionApprovalMCP, self.permission_decision_approve_for_session_approval_mcp) result["PermissionDecisionApproveForSessionApprovalMcpSampling"] = to_class(PermissionDecisionApproveForSessionApprovalMCPSampling, self.permission_decision_approve_for_session_approval_mcp_sampling) result["PermissionDecisionApproveForSessionApprovalMemory"] = to_class(PermissionDecisionApproveForSessionApprovalMemory, self.permission_decision_approve_for_session_approval_memory) @@ -30359,6 +30768,7 @@ def to_dict(self) -> dict: result["PermissionsLocationsAddToolApprovalDetailsCustomTool"] = to_class(PermissionsLocationsAddToolApprovalDetailsCustomTool, self.permissions_locations_add_tool_approval_details_custom_tool) result["PermissionsLocationsAddToolApprovalDetailsExtensionManagement"] = to_class(PermissionsLocationsAddToolApprovalDetailsExtensionManagement, self.permissions_locations_add_tool_approval_details_extension_management) result["PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess"] = to_class(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess, self.permissions_locations_add_tool_approval_details_extension_permission_access) + result["PermissionsLocationsAddToolApprovalDetailsFactory"] = to_class(PermissionsLocationsAddToolApprovalDetailsFactory, self.permissions_locations_add_tool_approval_details_factory) result["PermissionsLocationsAddToolApprovalDetailsMcp"] = to_class(PermissionsLocationsAddToolApprovalDetailsMCP, self.permissions_locations_add_tool_approval_details_mcp) result["PermissionsLocationsAddToolApprovalDetailsMcpSampling"] = to_class(PermissionsLocationsAddToolApprovalDetailsMCPSampling, self.permissions_locations_add_tool_approval_details_mcp_sampling) result["PermissionsLocationsAddToolApprovalDetailsMemory"] = to_class(PermissionsLocationsAddToolApprovalDetailsMemory, self.permissions_locations_add_tool_approval_details_memory) @@ -30935,7 +31345,7 @@ def _load_PermissionDecision(obj: Any) -> "PermissionDecision": case _: raise ValueError(f"Unknown PermissionDecision kind: {kind!r}") # Approval to persist for this location -PermissionDecisionApproveForLocationApproval = PermissionDecisionApproveForLocationApprovalCommands | PermissionDecisionApproveForLocationApprovalRead | PermissionDecisionApproveForLocationApprovalWrite | PermissionDecisionApproveForLocationApprovalMCP | PermissionDecisionApproveForLocationApprovalMCPSampling | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess +PermissionDecisionApproveForLocationApproval = PermissionDecisionApproveForLocationApprovalCommands | PermissionDecisionApproveForLocationApprovalRead | PermissionDecisionApproveForLocationApprovalWrite | PermissionDecisionApproveForLocationApprovalMCP | PermissionDecisionApproveForLocationApprovalMCPSampling | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalFactory | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionDecisionApproveForLocationApproval": assert isinstance(obj, dict) @@ -30949,11 +31359,12 @@ def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionD case "memory": return PermissionDecisionApproveForLocationApprovalMemory.from_dict(obj) case "custom-tool": return PermissionDecisionApproveForLocationApprovalCustomTool.from_dict(obj) case "extension-management": return PermissionDecisionApproveForLocationApprovalExtensionManagement.from_dict(obj) + case "factory": return PermissionDecisionApproveForLocationApprovalFactory.from_dict(obj) case "extension-permission-access": return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown PermissionDecisionApproveForLocationApproval kind: {kind!r}") # Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) -PermissionDecisionApproveForSessionApproval = PermissionDecisionApproveForSessionApprovalCommands | PermissionDecisionApproveForSessionApprovalRead | PermissionDecisionApproveForSessionApprovalWrite | PermissionDecisionApproveForSessionApprovalMCP | PermissionDecisionApproveForSessionApprovalMCPSampling | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess +PermissionDecisionApproveForSessionApproval = PermissionDecisionApproveForSessionApprovalCommands | PermissionDecisionApproveForSessionApprovalRead | PermissionDecisionApproveForSessionApprovalWrite | PermissionDecisionApproveForSessionApprovalMCP | PermissionDecisionApproveForSessionApprovalMCPSampling | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement | PermissionDecisionApproveForSessionApprovalFactory | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDecisionApproveForSessionApproval": assert isinstance(obj, dict) @@ -30967,11 +31378,12 @@ def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDe case "memory": return PermissionDecisionApproveForSessionApprovalMemory.from_dict(obj) case "custom-tool": return PermissionDecisionApproveForSessionApprovalCustomTool.from_dict(obj) case "extension-management": return PermissionDecisionApproveForSessionApprovalExtensionManagement.from_dict(obj) + case "factory": return PermissionDecisionApproveForSessionApprovalFactory.from_dict(obj) case "extension-permission-access": return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown PermissionDecisionApproveForSessionApproval kind: {kind!r}") # Tool approval to persist and apply -PermissionsLocationsAddToolApprovalDetails = PermissionsLocationsAddToolApprovalDetailsCommands | PermissionsLocationsAddToolApprovalDetailsRead | PermissionsLocationsAddToolApprovalDetailsWrite | PermissionsLocationsAddToolApprovalDetailsMCP | PermissionsLocationsAddToolApprovalDetailsMCPSampling | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess +PermissionsLocationsAddToolApprovalDetails = PermissionsLocationsAddToolApprovalDetailsCommands | PermissionsLocationsAddToolApprovalDetailsRead | PermissionsLocationsAddToolApprovalDetailsWrite | PermissionsLocationsAddToolApprovalDetailsMCP | PermissionsLocationsAddToolApprovalDetailsMCPSampling | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement | PermissionsLocationsAddToolApprovalDetailsFactory | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLocationsAddToolApprovalDetails": assert isinstance(obj, dict) @@ -30985,6 +31397,7 @@ def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLo case "memory": return PermissionsLocationsAddToolApprovalDetailsMemory.from_dict(obj) case "custom-tool": return PermissionsLocationsAddToolApprovalDetailsCustomTool.from_dict(obj) case "extension-management": return PermissionsLocationsAddToolApprovalDetailsExtensionManagement.from_dict(obj) + case "factory": return PermissionsLocationsAddToolApprovalDetailsFactory.from_dict(obj) case "extension-permission-access": return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown PermissionsLocationsAddToolApprovalDetails kind: {kind!r}") @@ -31290,6 +31703,26 @@ async def discover(self, params: MCPDiscoverRequest, *, timeout: float | None = return MCPDiscoverResult.from_dict(await self._client.request("mcp.discover", params_dict, **_timeout_kwargs(timeout))) +# Experimental: this API group is experimental and may change or be removed. +class ServerExtensionsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def discover(self, *, timeout: float | None = None) -> DiscoveredExtensions: + "Discovers user and enabled installed-plugin extensions from persisted Copilot home state, including enablement preferences. Launch-scoped additional plugins are not included.\n\nReturns:\n Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included." + return DiscoveredExtensions.from_dict(await self._client.request("extensions.discover", {}, **_timeout_kwargs(timeout))) + + async def enable(self, params: DiscoveredExtensionsEnableRequest, *, timeout: float | None = None) -> None: + "Persistently enables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.enable to update them.\n\nArgs:\n params: Source-qualified extension identifiers to persistently enable for future sessions." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("extensions.enable", params_dict, **_timeout_kwargs(timeout)) + + async def disable(self, params: DiscoveredExtensionsDisableRequest, *, timeout: float | None = None) -> None: + "Persistently disables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.disable to update them.\n\nArgs:\n params: Source-qualified extension identifiers to persistently disable for future sessions." + params_dict = {k: v for k, v in params.to_dict().items() if v is not None} + await self._client.request("extensions.disable", params_dict, **_timeout_kwargs(timeout)) + + # Experimental: this API group is experimental and may change or be removed. class ServerPluginsMarketplacesApi: def __init__(self, client: "JsonRpcClient"): @@ -31636,6 +32069,7 @@ def __init__(self, client: "JsonRpcClient"): self.account = ServerAccountApi(client) self.secrets = ServerSecretsApi(client) self.mcp = ServerMcpApi(client) + self.extensions = ServerExtensionsApi(client) self.plugins = ServerPluginsApi(client) self.skills = ServerSkillsApi(client) self.agents = ServerAgentsApi(client) @@ -32104,6 +32538,12 @@ async def list(self, params: SessionAgentListRequest | None = None, *, timeout: params_dict["sessionId"] = self._session_id return AgentList.from_dict(await self._client.request("session.agent.list", params_dict, **_timeout_kwargs(timeout))) + async def set_prompt(self, params: AgentSetPromptRequest, *, timeout: float | None = None) -> None: + "Sets an in-memory authored prompt override for an available agent. For built-in agents, this replaces only the static base prompt while preserving runtime-owned dynamic prompt composition and behavior. The special `general-purpose` agent is not overrideable. Overrides are not persisted; resumed and forked sessions start without them, so the host must re-apply them.\n\nArgs:\n params: An in-memory authored prompt override for an available agent." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.agent.setPrompt", params_dict, **_timeout_kwargs(timeout)) + async def get_current(self, *, timeout: float | None = None) -> AgentGetCurrentResult: "Gets the currently selected custom agent for the session.\n\nReturns:\n The currently selected custom agent, or null when using the default agent." return AgentGetCurrentResult.from_dict(await self._client.request("session.agent.getCurrent", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -32233,6 +32673,12 @@ async def handle_pending_request(self, params: MCPOauthHandlePendingRequest, *, params_dict["sessionId"] = self._session_id return MCPOauthHandlePendingResult.from_dict(await self._client.request("session.mcp.oauth.handlePendingRequest", params_dict, **_timeout_kwargs(timeout))) + async def authentication_state_changed(self, params: MCPOauthAuthenticationStateChangedRequest, *, timeout: float | None = None) -> None: + "Notifies the session that MCP OAuth authentication succeeded and updated credentials were persisted, so cached tool definitions can be refreshed.\n\nArgs:\n params: Identifies the MCP server whose persisted OAuth credentials were updated." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + await self._client.request("session.mcp.oauth.authenticationStateChanged", params_dict, **_timeout_kwargs(timeout)) + async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = None) -> MCPOauthLoginResult: "Starts OAuth authentication for a remote MCP server.\n\nArgs:\n params: Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection.\n\nReturns:\n OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -33026,7 +33472,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._session_id = session_id async def read(self, params: EventLogReadRequest, *, timeout: float | None = None) -> EventsReadResult: - "Reads a batch of session events from a cursor, optionally waiting for new events.\n\nArgs:\n params: Cursor, batch size, and optional long-poll/filter parameters for reading session events.\n\nReturns:\n Batch of session events returned by a read, with cursor and continuation metadata." + "Reads a batch of session events from a cursor, optionally waiting for new events. Supports tail-first reads via `direction: backward`.\n\nArgs:\n params: Cursor, batch size, and optional long-poll/filter parameters for reading session events.\n\nReturns:\n Batch of session events returned by a read, with cursor and continuation metadata." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return EventsReadResult.from_dict(await self._client.request("session.eventLog.read", params_dict, **_timeout_kwargs(timeout))) @@ -33705,6 +34151,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "AgentReloadResult", "AgentSelectRequest", "AgentSelectResult", + "AgentSetPromptRequest", "AgentsDiscoverRequest", "AgentsGetDiscoveryPathsRequest", "AllowAllPermissionSetResult", @@ -33782,6 +34229,13 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "DebugCollectLogsSkippedEntry", "DebugCollectLogsSource", "DiscoveredCanvas", + "DiscoveredExtension", + "DiscoveredExtensionMode", + "DiscoveredExtensionPlugin", + "DiscoveredExtensionSource", + "DiscoveredExtensions", + "DiscoveredExtensionsDisableRequest", + "DiscoveredExtensionsEnableRequest", "DiscoveredMCPServer", "DiscoveredMCPServerType", "EnqueueCommandParams", @@ -33796,6 +34250,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "EventLogTypes", "EventsAgentScope", "EventsCursorStatus", + "EventsReadDirection", "EventsReadResult", "ExecuteCommandParams", "ExecuteCommandResult", @@ -33999,6 +34454,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "MCPIsServerRunningResult", "MCPListToolsRequest", "MCPListToolsResult", + "MCPOauthAuthenticationStateChangedRequest", "MCPOauthHandlePendingRequest", "MCPOauthHandlePendingResult", "MCPOauthLoginRequest", @@ -34143,6 +34599,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionApproveForLocationApprovalExtensionManagementKind", "PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess", "PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind", + "PermissionDecisionApproveForLocationApprovalFactory", + "PermissionDecisionApproveForLocationApprovalFactoryKind", "PermissionDecisionApproveForLocationApprovalMCP", "PermissionDecisionApproveForLocationApprovalMCPKind", "PermissionDecisionApproveForLocationApprovalMCPSampling", @@ -34160,6 +34618,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionApproveForSessionApprovalCustomTool", "PermissionDecisionApproveForSessionApprovalExtensionManagement", "PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess", + "PermissionDecisionApproveForSessionApprovalFactory", "PermissionDecisionApproveForSessionApprovalMCP", "PermissionDecisionApproveForSessionApprovalMCPSampling", "PermissionDecisionApproveForSessionApprovalMemory", @@ -34229,6 +34688,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionsLocationsAddToolApprovalDetailsCustomTool", "PermissionsLocationsAddToolApprovalDetailsExtensionManagement", "PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess", + "PermissionsLocationsAddToolApprovalDetailsFactory", "PermissionsLocationsAddToolApprovalDetailsMCP", "PermissionsLocationsAddToolApprovalDetailsMCPSampling", "PermissionsLocationsAddToolApprovalDetailsMemory", @@ -34442,6 +34902,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "ServerAgentRegistryApi", "ServerAgentsApi", "ServerCommandsApi", + "ServerExtensionsApi", "ServerInstructionSourceList", "ServerInstructionsApi", "ServerLlmInferenceApi", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index a9f6fe701..069a2ae84 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1318,6 +1318,8 @@ class AssistantMessageData: content: str message_id: str api_call_id: str | None = None + chunk_count: int | None = None + chunk_index: int | None = None # Experimental: this field is part of an experimental API and may change or be removed. citations: Citations | None = None client_request_id: str | None = None @@ -1344,6 +1346,8 @@ def from_dict(obj: Any) -> "AssistantMessageData": content = from_str(obj.get("content")) message_id = from_str(obj.get("messageId")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) + chunk_count = from_union([from_none, from_int], obj.get("chunkCount")) + chunk_index = from_union([from_none, from_int], obj.get("chunkIndex")) citations = from_union([from_none, Citations.from_dict], obj.get("citations")) client_request_id = from_union([from_none, from_str], obj.get("clientRequestId")) encrypted_content = from_union([from_none, from_str], obj.get("encryptedContent")) @@ -1365,6 +1369,8 @@ def from_dict(obj: Any) -> "AssistantMessageData": content=content, message_id=message_id, api_call_id=api_call_id, + chunk_count=chunk_count, + chunk_index=chunk_index, citations=citations, client_request_id=client_request_id, encrypted_content=encrypted_content, @@ -1390,6 +1396,10 @@ def to_dict(self) -> dict: result["messageId"] = from_str(self.message_id) if self.api_call_id is not None: result["apiCallId"] = from_union([from_none, from_str], self.api_call_id) + if self.chunk_count is not None: + result["chunkCount"] = from_union([from_none, to_int], self.chunk_count) + if self.chunk_index is not None: + result["chunkIndex"] = from_union([from_none, to_int], self.chunk_index) if self.citations is not None: result["citations"] = from_union([from_none, lambda x: to_class(Citations, x)], self.citations) if self.client_request_id is not None: @@ -3426,6 +3436,65 @@ def to_dict(self) -> dict: return result +@dataclass +class FactoryPermissionPhase: + "A declared phase shown in a factory permission prompt." + title: str + detail: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "FactoryPermissionPhase": + assert isinstance(obj, dict) + title = from_str(obj.get("title")) + detail = from_union([from_none, from_str], obj.get("detail")) + return FactoryPermissionPhase( + title=title, + detail=detail, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["title"] = from_str(self.title) + if self.detail is not None: + result["detail"] = from_union([from_none, from_str], self.detail) + return result + + +@dataclass +class GitHubMcpToolConfig: + "Per-session configuration for the built-in GitHub MCP server" + additional_tools: list[str] | None = None + additional_toolsets: list[str] | None = None + enable_all_tools: bool | None = None + enable_insiders_mode: bool | None = None + + @staticmethod + def from_dict(obj: Any) -> "GitHubMcpToolConfig": + assert isinstance(obj, dict) + additional_tools = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("additionalTools")) + additional_toolsets = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("additionalToolsets")) + enable_all_tools = from_union([from_none, from_bool], obj.get("enableAllTools")) + enable_insiders_mode = from_union([from_none, from_bool], obj.get("enableInsidersMode")) + return GitHubMcpToolConfig( + additional_tools=additional_tools, + additional_toolsets=additional_toolsets, + enable_all_tools=enable_all_tools, + enable_insiders_mode=enable_insiders_mode, + ) + + def to_dict(self) -> dict: + result: dict = {} + if self.additional_tools is not None: + result["additionalTools"] = from_union([from_none, lambda x: from_list(from_str, x)], self.additional_tools) + if self.additional_toolsets is not None: + result["additionalToolsets"] = from_union([from_none, lambda x: from_list(from_str, x)], self.additional_toolsets) + if self.enable_all_tools is not None: + result["enableAllTools"] = from_union([from_none, from_bool], self.enable_all_tools) + if self.enable_insiders_mode is not None: + result["enableInsidersMode"] = from_union([from_none, from_bool], self.enable_insiders_mode) + return result + + @dataclass class GitHubRepoRef: "Pointer to a GitHub repository." @@ -4677,6 +4746,103 @@ def to_dict(self) -> dict: return result +@dataclass +class PermissionPromptRequestFactory: + "Factory run or authoring permission prompt" + approval_key: str + can_persist_approval: bool + description: str + kind: ClassVar[str] = "factory" + name: str + operation: FactoryPermissionOperation + phases: list[FactoryPermissionPhase] + # Experimental: this field is part of an experimental API and may change or be removed. + auto_approval: PermissionAutoApproval | None = None + declared_max_ai_credits: float | None = None + declared_max_concurrent_subagents: int | None = None + declared_max_total_subagents: int | None = None + declared_timeout_seconds: float | None = None + managed_approval_required: bool | None = None + max_ai_credits: float | None = None + max_concurrent_subagents: int | None = None + max_total_subagents: int | None = None + timeout_seconds: float | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestFactory": + assert isinstance(obj, dict) + approval_key = from_str(obj.get("approvalKey")) + can_persist_approval = from_bool(obj.get("canPersistApproval")) + description = from_str(obj.get("description")) + name = from_str(obj.get("name")) + operation = parse_enum(FactoryPermissionOperation, obj.get("operation")) + phases = from_list(FactoryPermissionPhase.from_dict, obj.get("phases")) + auto_approval = from_union([from_none, PermissionAutoApproval.from_dict], obj.get("autoApproval")) + declared_max_ai_credits = from_union([from_none, from_float], obj.get("declaredMaxAiCredits")) + declared_max_concurrent_subagents = from_union([from_none, from_int], obj.get("declaredMaxConcurrentSubagents")) + declared_max_total_subagents = from_union([from_none, from_int], obj.get("declaredMaxTotalSubagents")) + declared_timeout_seconds = from_union([from_none, from_float], obj.get("declaredTimeoutSeconds")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) + max_ai_credits = from_union([from_none, from_float], obj.get("maxAiCredits")) + max_concurrent_subagents = from_union([from_none, from_int], obj.get("maxConcurrentSubagents")) + max_total_subagents = from_union([from_none, from_int], obj.get("maxTotalSubagents")) + timeout_seconds = from_union([from_none, from_float], obj.get("timeoutSeconds")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestFactory( + approval_key=approval_key, + can_persist_approval=can_persist_approval, + description=description, + name=name, + operation=operation, + phases=phases, + auto_approval=auto_approval, + declared_max_ai_credits=declared_max_ai_credits, + declared_max_concurrent_subagents=declared_max_concurrent_subagents, + declared_max_total_subagents=declared_max_total_subagents, + declared_timeout_seconds=declared_timeout_seconds, + managed_approval_required=managed_approval_required, + max_ai_credits=max_ai_credits, + max_concurrent_subagents=max_concurrent_subagents, + max_total_subagents=max_total_subagents, + timeout_seconds=timeout_seconds, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["approvalKey"] = from_str(self.approval_key) + result["canPersistApproval"] = from_bool(self.can_persist_approval) + result["description"] = from_str(self.description) + result["kind"] = self.kind + result["name"] = from_str(self.name) + result["operation"] = to_enum(FactoryPermissionOperation, self.operation) + result["phases"] = from_list(lambda x: to_class(FactoryPermissionPhase, x), self.phases) + if self.auto_approval is not None: + result["autoApproval"] = from_union([from_none, lambda x: to_class(PermissionAutoApproval, x)], self.auto_approval) + if self.declared_max_ai_credits is not None: + result["declaredMaxAiCredits"] = from_union([from_none, to_float], self.declared_max_ai_credits) + if self.declared_max_concurrent_subagents is not None: + result["declaredMaxConcurrentSubagents"] = from_union([from_none, to_int], self.declared_max_concurrent_subagents) + if self.declared_max_total_subagents is not None: + result["declaredMaxTotalSubagents"] = from_union([from_none, to_int], self.declared_max_total_subagents) + if self.declared_timeout_seconds is not None: + result["declaredTimeoutSeconds"] = from_union([from_none, to_float], self.declared_timeout_seconds) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) + if self.max_ai_credits is not None: + result["maxAiCredits"] = from_union([from_none, to_float], self.max_ai_credits) + if self.max_concurrent_subagents is not None: + result["maxConcurrentSubagents"] = from_union([from_none, to_int], self.max_concurrent_subagents) + if self.max_total_subagents is not None: + result["maxTotalSubagents"] = from_union([from_none, to_int], self.max_total_subagents) + if self.timeout_seconds is not None: + result["timeoutSeconds"] = from_union([from_none, to_float], self.timeout_seconds) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + @dataclass class PermissionPromptRequestHook: "Hook confirmation permission prompt" @@ -5119,6 +5285,97 @@ def to_dict(self) -> dict: return result +@dataclass +class PermissionRequestFactory: + "Factory run or authoring permission request" + approval_key: str + can_persist_approval: bool + description: str + kind: ClassVar[str] = "factory" + name: str + operation: FactoryPermissionOperation + phases: list[FactoryPermissionPhase] + declared_max_ai_credits: float | None = None + declared_max_concurrent_subagents: int | None = None + declared_max_total_subagents: int | None = None + declared_timeout_seconds: float | None = None + max_ai_credits: float | None = None + max_concurrent_subagents: int | None = None + max_total_subagents: int | None = None + timeout_seconds: float | None = None + tool_call_id: str | None = None + managed_approval_required: bool | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestFactory": + assert isinstance(obj, dict) + approval_key = from_str(obj.get("approvalKey")) + can_persist_approval = from_bool(obj.get("canPersistApproval")) + description = from_str(obj.get("description")) + name = from_str(obj.get("name")) + operation = parse_enum(FactoryPermissionOperation, obj.get("operation")) + phases = from_list(FactoryPermissionPhase.from_dict, obj.get("phases")) + declared_max_ai_credits = from_union([from_none, from_float], obj.get("declaredMaxAiCredits")) + declared_max_concurrent_subagents = from_union([from_none, from_int], obj.get("declaredMaxConcurrentSubagents")) + declared_max_total_subagents = from_union([from_none, from_int], obj.get("declaredMaxTotalSubagents")) + declared_timeout_seconds = from_union([from_none, from_float], obj.get("declaredTimeoutSeconds")) + max_ai_credits = from_union([from_none, from_float], obj.get("maxAiCredits")) + max_concurrent_subagents = from_union([from_none, from_int], obj.get("maxConcurrentSubagents")) + max_total_subagents = from_union([from_none, from_int], obj.get("maxTotalSubagents")) + timeout_seconds = from_union([from_none, from_float], obj.get("timeoutSeconds")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + managed_approval_required = from_union([from_none, from_bool], obj.get("managedApprovalRequired")) + return PermissionRequestFactory( + approval_key=approval_key, + can_persist_approval=can_persist_approval, + description=description, + name=name, + operation=operation, + phases=phases, + declared_max_ai_credits=declared_max_ai_credits, + declared_max_concurrent_subagents=declared_max_concurrent_subagents, + declared_max_total_subagents=declared_max_total_subagents, + declared_timeout_seconds=declared_timeout_seconds, + max_ai_credits=max_ai_credits, + max_concurrent_subagents=max_concurrent_subagents, + max_total_subagents=max_total_subagents, + timeout_seconds=timeout_seconds, + tool_call_id=tool_call_id, + managed_approval_required=managed_approval_required, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["approvalKey"] = from_str(self.approval_key) + result["canPersistApproval"] = from_bool(self.can_persist_approval) + result["description"] = from_str(self.description) + result["kind"] = self.kind + result["name"] = from_str(self.name) + result["operation"] = to_enum(FactoryPermissionOperation, self.operation) + result["phases"] = from_list(lambda x: to_class(FactoryPermissionPhase, x), self.phases) + if self.declared_max_ai_credits is not None: + result["declaredMaxAiCredits"] = from_union([from_none, to_float], self.declared_max_ai_credits) + if self.declared_max_concurrent_subagents is not None: + result["declaredMaxConcurrentSubagents"] = from_union([from_none, to_int], self.declared_max_concurrent_subagents) + if self.declared_max_total_subagents is not None: + result["declaredMaxTotalSubagents"] = from_union([from_none, to_int], self.declared_max_total_subagents) + if self.declared_timeout_seconds is not None: + result["declaredTimeoutSeconds"] = from_union([from_none, to_float], self.declared_timeout_seconds) + if self.max_ai_credits is not None: + result["maxAiCredits"] = from_union([from_none, to_float], self.max_ai_credits) + if self.max_concurrent_subagents is not None: + result["maxConcurrentSubagents"] = from_union([from_none, to_int], self.max_concurrent_subagents) + if self.max_total_subagents is not None: + result["maxTotalSubagents"] = from_union([from_none, to_int], self.max_total_subagents) + if self.timeout_seconds is not None: + result["timeoutSeconds"] = from_union([from_none, to_float], self.timeout_seconds) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.managed_approval_required is not None: + result["managedApprovalRequired"] = from_union([from_none, from_bool], self.managed_approval_required) + return result + + @dataclass class PermissionRequestHook: "Hook confirmation permission request" @@ -6925,6 +7182,7 @@ class SessionStartData: context: WorkingDirectoryContext | None = None context_tier: ContextTier | None = None detached_from_spawning_parent_session_id: str | None = None + github_mcp_tool_config: GitHubMcpToolConfig | None = None reasoning_effort: str | None = None reasoning_summary: ReasoningSummary | None = None remote_steerable: bool | None = None @@ -6944,6 +7202,7 @@ def from_dict(obj: Any) -> "SessionStartData": context = from_union([from_none, WorkingDirectoryContext.from_dict], obj.get("context")) context_tier = from_union([from_none, lambda x: parse_enum(ContextTier, x)], obj.get("contextTier")) detached_from_spawning_parent_session_id = from_union([from_none, from_str], obj.get("detachedFromSpawningParentSessionId")) + github_mcp_tool_config = from_union([from_none, GitHubMcpToolConfig.from_dict], obj.get("githubMcpToolConfig")) reasoning_effort = from_union([from_none, from_str], obj.get("reasoningEffort")) reasoning_summary = from_union([from_none, lambda x: parse_enum(ReasoningSummary, x)], obj.get("reasoningSummary")) remote_steerable = from_union([from_none, from_bool], obj.get("remoteSteerable")) @@ -6960,6 +7219,7 @@ def from_dict(obj: Any) -> "SessionStartData": context=context, context_tier=context_tier, detached_from_spawning_parent_session_id=detached_from_spawning_parent_session_id, + github_mcp_tool_config=github_mcp_tool_config, reasoning_effort=reasoning_effort, reasoning_summary=reasoning_summary, remote_steerable=remote_steerable, @@ -6983,6 +7243,8 @@ def to_dict(self) -> dict: result["contextTier"] = from_union([from_none, lambda x: to_enum(ContextTier, x)], self.context_tier) if self.detached_from_spawning_parent_session_id is not None: result["detachedFromSpawningParentSessionId"] = from_union([from_none, from_str], self.detached_from_spawning_parent_session_id) + if self.github_mcp_tool_config is not None: + result["githubMcpToolConfig"] = from_union([from_none, lambda x: to_class(GitHubMcpToolConfig, x)], self.github_mcp_tool_config) if self.reasoning_effort is not None: result["reasoningEffort"] = from_union([from_none, from_str], self.reasoning_effort) if self.reasoning_summary is not None: @@ -7861,6 +8123,66 @@ def to_dict(self) -> dict: return result +@dataclass +class SystemNotificationFactoryCompleted: + "System notification metadata for a factory execution attempt that reached a terminal state." + attempt: int + consumed_nano_aiu: int + consumed_subagents: int + elapsed_ms: int + factory_name: str + run_id: str + status: SystemNotificationFactoryCompletedStatus + type: ClassVar[str] = "factory_completed" + failure: Any = None + result_preview: str | None = None + retry_guidance: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationFactoryCompleted": + assert isinstance(obj, dict) + attempt = from_int(obj.get("attempt")) + consumed_nano_aiu = from_int(obj.get("consumedNanoAiu")) + consumed_subagents = from_int(obj.get("consumedSubagents")) + elapsed_ms = from_int(obj.get("elapsedMs")) + factory_name = from_str(obj.get("factoryName")) + run_id = from_str(obj.get("runId")) + status = parse_enum(SystemNotificationFactoryCompletedStatus, obj.get("status")) + failure = obj.get("failure") + result_preview = from_union([from_none, from_str], obj.get("resultPreview")) + retry_guidance = from_union([from_none, from_str], obj.get("retryGuidance")) + return SystemNotificationFactoryCompleted( + attempt=attempt, + consumed_nano_aiu=consumed_nano_aiu, + consumed_subagents=consumed_subagents, + elapsed_ms=elapsed_ms, + factory_name=factory_name, + run_id=run_id, + status=status, + failure=failure, + result_preview=result_preview, + retry_guidance=retry_guidance, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["attempt"] = to_int(self.attempt) + result["consumedNanoAiu"] = to_int(self.consumed_nano_aiu) + result["consumedSubagents"] = to_int(self.consumed_subagents) + result["elapsedMs"] = to_int(self.elapsed_ms) + result["factoryName"] = from_str(self.factory_name) + result["runId"] = from_str(self.run_id) + result["status"] = to_enum(SystemNotificationFactoryCompletedStatus, self.status) + result["type"] = self.type + if self.failure is not None: + result["failure"] = self.failure + if self.result_preview is not None: + result["resultPreview"] = from_union([from_none, from_str], self.result_preview) + if self.retry_guidance is not None: + result["retryGuidance"] = from_union([from_none, from_str], self.retry_guidance) + return result + + @dataclass class SystemNotificationInstructionDiscovered: "System notification metadata for an instruction file discovered during tool access, including source, trigger file, and tool." @@ -9197,6 +9519,28 @@ def to_dict(self) -> dict: return result +@dataclass +class UserToolSessionApprovalFactory: + "Session-scoped factory approval, optionally narrowed by approval key." + kind: ClassVar[str] = "factory" + approval_key: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalFactory": + assert isinstance(obj, dict) + approval_key = from_union([from_none, from_str], obj.get("approvalKey")) + return UserToolSessionApprovalFactory( + approval_key=approval_key, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.approval_key is not None: + result["approvalKey"] = from_union([from_none, from_str], self.approval_key) + return result + + @dataclass class UserToolSessionApprovalMcp: "Session-scoped tool-approval rule for an MCP server tool, or all tools on the server when `toolName` is null." @@ -9378,6 +9722,7 @@ def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": case "path": return PermissionPromptRequestPath.from_dict(obj) case "hook": return PermissionPromptRequestHook.from_dict(obj) case "extension-management": return PermissionPromptRequestExtensionManagement.from_dict(obj) + case "factory": return PermissionPromptRequestFactory.from_dict(obj) case "extension-permission-access": return PermissionPromptRequestExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown PermissionPromptRequest kind: {kind!r}") @@ -9395,6 +9740,7 @@ def _load_PermissionRequest(obj: Any) -> "PermissionRequest": case "custom-tool": return PermissionRequestCustomTool.from_dict(obj) case "hook": return PermissionRequestHook.from_dict(obj) case "extension-management": return PermissionRequestExtensionManagement.from_dict(obj) + case "factory": return PermissionRequestFactory.from_dict(obj) case "extension-permission-access": return PermissionRequestExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown PermissionRequest kind: {kind!r}") @@ -9425,6 +9771,7 @@ def _load_SystemNotification(obj: Any) -> "SystemNotification": case "shell_completed": return SystemNotificationShellCompleted.from_dict(obj) case "shell_detached_completed": return SystemNotificationShellDetachedCompleted.from_dict(obj) case "instruction_discovered": return SystemNotificationInstructionDiscovered.from_dict(obj) + case "factory_completed": return SystemNotificationFactoryCompleted.from_dict(obj) case "unclassified": return SystemNotificationUnclassified.from_dict(obj) case _: raise ValueError(f"Unknown SystemNotification type: {kind!r}") @@ -9454,6 +9801,7 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": case "memory": return UserToolSessionApprovalMemory.from_dict(obj) case "custom-tool": return UserToolSessionApprovalCustomTool.from_dict(obj) case "extension-management": return UserToolSessionApprovalExtensionManagement.from_dict(obj) + case "factory": return UserToolSessionApprovalFactory.from_dict(obj) case "extension-permission-access": return UserToolSessionApprovalExtensionPermissionAccess.from_dict(obj) case _: raise ValueError(f"Unknown UserToolSessionApproval kind: {kind!r}") @@ -9471,11 +9819,11 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": # Derived user-facing permission prompt details for UI consumers -PermissionPromptRequest = PermissionPromptRequestCommands | PermissionPromptRequestWrite | PermissionPromptRequestRead | PermissionPromptRequestMcp | PermissionPromptRequestUrl | PermissionPromptRequestMemory | PermissionPromptRequestCustomTool | PermissionPromptRequestPath | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement | PermissionPromptRequestExtensionPermissionAccess +PermissionPromptRequest = PermissionPromptRequestCommands | PermissionPromptRequestWrite | PermissionPromptRequestRead | PermissionPromptRequestMcp | PermissionPromptRequestUrl | PermissionPromptRequestMemory | PermissionPromptRequestCustomTool | PermissionPromptRequestPath | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement | PermissionPromptRequestFactory | PermissionPromptRequestExtensionPermissionAccess # Details of the permission being requested -PermissionRequest = PermissionRequestShell | PermissionRequestWrite | PermissionRequestRead | PermissionRequestMcp | PermissionRequestUrl | PermissionRequestMemory | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement | PermissionRequestExtensionPermissionAccess +PermissionRequest = PermissionRequestShell | PermissionRequestWrite | PermissionRequestRead | PermissionRequestMcp | PermissionRequestUrl | PermissionRequestMemory | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement | PermissionRequestFactory | PermissionRequestExtensionPermissionAccess # Location within a cited source (character, page, or content-block range) that supports a span. @@ -9483,11 +9831,11 @@ def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": # Structured metadata identifying what triggered this notification -SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered | SystemNotificationUnclassified +SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered | SystemNotificationFactoryCompleted | SystemNotificationUnclassified # The approval to add as a session-scoped rule -UserToolSessionApproval = UserToolSessionApprovalCommands | UserToolSessionApprovalRead | UserToolSessionApprovalWrite | UserToolSessionApprovalMcp | UserToolSessionApprovalMemory | UserToolSessionApprovalCustomTool | UserToolSessionApprovalExtensionManagement | UserToolSessionApprovalExtensionPermissionAccess +UserToolSessionApproval = UserToolSessionApprovalCommands | UserToolSessionApprovalRead | UserToolSessionApprovalWrite | UserToolSessionApprovalMcp | UserToolSessionApprovalMemory | UserToolSessionApprovalCustomTool | UserToolSessionApprovalExtensionManagement | UserToolSessionApprovalFactory | UserToolSessionApprovalExtensionPermissionAccess # The embedded resource contents, either text or base64-encoded binary @@ -9724,6 +10072,14 @@ class ExtensionsLoadedExtensionStatus(Enum): STARTING = "starting" +class FactoryPermissionOperation(Enum): + "Operation gated by a factory permission request." + # Running a registered factory, which spends subagents, active time, and AI credits under the approved limits. + RUN = "run" + # Authoring a factory, which writes JavaScript into a session-scoped extension and loads it. + AUTHOR = "author" + + class HandoffSourceType(Enum): "Origin type of the session being handed off" # The handoff originated from a remote session. @@ -10030,6 +10386,18 @@ class SystemNotificationAgentCompletedStatus(Enum): FAILED = "failed" +class SystemNotificationFactoryCompletedStatus(Enum): + "Terminal status reached by a factory execution attempt." + # The factory completed successfully. + COMPLETED = "completed" + # The factory was halted. + HALTED = "halted" + # The factory was cancelled. + CANCELLED = "cancelled" + # The factory failed. + ERROR = "error" + + class TaskCompletionOutcome(Enum): "Semantic result of evaluating a task completion request" # The completion request was accepted and the objective is complete. @@ -10382,7 +10750,10 @@ def session_event_to_dict(x: SessionEvent) -> Any: "ExtensionsLoadedExtensionStatus", "ExternalToolCompletedData", "ExternalToolRequestedData", + "FactoryPermissionOperation", + "FactoryPermissionPhase", "FactoryRunUpdatedData", + "GitHubMcpToolConfig", "GitHubRepoRef", "HandoffRepository", "HandoffSourceType", @@ -10444,6 +10815,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "PermissionPromptRequestCustomTool", "PermissionPromptRequestExtensionManagement", "PermissionPromptRequestExtensionPermissionAccess", + "PermissionPromptRequestFactory", "PermissionPromptRequestHook", "PermissionPromptRequestMcp", "PermissionPromptRequestMemory", @@ -10456,6 +10828,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "PermissionRequestCustomTool", "PermissionRequestExtensionManagement", "PermissionRequestExtensionPermissionAccess", + "PermissionRequestFactory", "PermissionRequestHook", "PermissionRequestMcp", "PermissionRequestMemory", @@ -10561,6 +10934,8 @@ def session_event_to_dict(x: SessionEvent) -> Any: "SystemNotificationAgentCompletedStatus", "SystemNotificationAgentIdle", "SystemNotificationData", + "SystemNotificationFactoryCompleted", + "SystemNotificationFactoryCompletedStatus", "SystemNotificationInstructionDiscovered", "SystemNotificationNewInboxMessage", "SystemNotificationShellCompleted", @@ -10614,6 +10989,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "UserToolSessionApprovalCustomTool", "UserToolSessionApprovalExtensionManagement", "UserToolSessionApprovalExtensionPermissionAccess", + "UserToolSessionApprovalFactory", "UserToolSessionApprovalMcp", "UserToolSessionApprovalMemory", "UserToolSessionApprovalRead", diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 0472ea0f7..cb0c4da54 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -56,6 +56,12 @@ pub mod rpc_methods { pub const MCP_CONFIG_RELOAD: &str = "mcp.config.reload"; /// `mcp.discover` pub const MCP_DISCOVER: &str = "mcp.discover"; + /// `extensions.discover` + pub const EXTENSIONS_DISCOVER: &str = "extensions.discover"; + /// `extensions.enable` + pub const EXTENSIONS_ENABLE: &str = "extensions.enable"; + /// `extensions.disable` + pub const EXTENSIONS_DISABLE: &str = "extensions.disable"; /// `plugins.list` pub const PLUGINS_LIST: &str = "plugins.list"; /// `plugins.install` @@ -307,6 +313,8 @@ pub mod rpc_methods { pub const SESSION_FLEET_START: &str = "session.fleet.start"; /// `session.agent.list` pub const SESSION_AGENT_LIST: &str = "session.agent.list"; + /// `session.agent.setPrompt` + pub const SESSION_AGENT_SETPROMPT: &str = "session.agent.setPrompt"; /// `session.agent.getCurrent` pub const SESSION_AGENT_GETCURRENT: &str = "session.agent.getCurrent"; /// `session.agent.select` @@ -387,6 +395,9 @@ pub mod rpc_methods { /// `session.mcp.oauth.handlePendingRequest` pub const SESSION_MCP_OAUTH_HANDLEPENDINGREQUEST: &str = "session.mcp.oauth.handlePendingRequest"; + /// `session.mcp.oauth.authenticationStateChanged` + pub const SESSION_MCP_OAUTH_AUTHENTICATIONSTATECHANGED: &str = + "session.mcp.oauth.authenticationStateChanged"; /// `session.mcp.oauth.login` pub const SESSION_MCP_OAUTH_LOGIN: &str = "session.mcp.oauth.login"; /// `session.mcp.oauth.respond` @@ -1306,6 +1317,23 @@ pub struct AgentSelectResult { pub agent: AgentInfo, } +/// An in-memory authored prompt override for an available agent. +/// +///

+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentSetPromptRequest { + /// Stable effective agent id. Plugin namespace separators are normalized. + pub id: String, + /// Replacement authored prompt. Empty text is valid. + pub prompt: String, +} + /// Optional project paths to include when enumerating agent discovery directories. /// ///
@@ -3339,6 +3367,94 @@ pub struct DebugCollectLogsResult { pub skipped_entries: Option>, } +/// Installed plugin that contributes a discovered extension. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DiscoveredExtensionPlugin { + /// Installed plugin name + pub name: String, +} + +/// Discovered extension metadata and persistent enablement state. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DiscoveredExtension { + /// Whether this extension's persistent per-ID preference is enabled + pub enabled: bool, + /// Source-qualified ID accepted by both server and session extension enablement methods + pub id: String, + /// Human-readable extension name + pub name: String, + /// Absolute path to the extension entry module, suitable for revealing it in a file manager + pub path: String, + /// Containing plugin metadata for plugin-contributed extensions + #[serde(skip_serializing_if = "Option::is_none")] + pub plugin: Option, + /// Discovery source + pub source: DiscoveredExtensionSource, +} + +/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DiscoveredExtensions { + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state + pub extensions: Vec, + /// Effective extension loading mode. Defaults to load_and_augment when unset. + pub mode: DiscoveredExtensionMode, +} + +/// Source-qualified extension identifiers to persistently disable for future sessions. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DiscoveredExtensionsDisableRequest { + /// Source-qualified user or plugin extension IDs to disable + pub ids: Vec, +} + +/// Source-qualified extension identifiers to persistently enable for future sessions. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DiscoveredExtensionsEnableRequest { + /// Source-qualified user or plugin extension IDs to enable + pub ids: Vec, +} + /// MCP server discovered by `mcp.discover`, with config source, optional plugin source, transport type, and enabled state. /// ///
@@ -3435,13 +3551,19 @@ pub struct EnvAuthInfo { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EventLogReadRequest { + /// Optional non-empty list of subagent identifiers. When provided, only events owned by one of these agents are returned; ownership recognizes the event envelope's agentId plus legacy data.agentId and data.parentToolCallId markers. This filter takes precedence over agentScope. + #[serde(skip_serializing_if = "Option::is_none")] + pub agent_ids: Option>, /// Agent-scope filter: 'primary' returns only main-agent events plus events whose type starts with 'subagent.' (matching the typed-subscription default behavior); 'all' returns events from all agents (matching wildcard-subscription behavior). Default is 'all' to preserve wildcard semantics for catch-up callers. #[serde(skip_serializing_if = "Option::is_none")] pub agent_scope: Option, /// Opaque cursor returned by a previous read. Omit on the first call to start from the beginning of the session's persisted history. #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, - /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). + /// Direction to page through the session's persisted event history. 'forward' (default) pages from the cursor toward newer events (or from the start of history when no cursor is given). 'backward' enables tail-first reads: with no cursor it returns the NEWEST `max` events, and the returned cursor pages toward OLDER events on subsequent backward reads. Events within a returned batch are always in chronological (oldest-to-newest) order, even for a backward read. Backward reads cover PERSISTED history only; ephemeral events are never returned by a backward read. `direction` selects the INITIAL read only: the returned cursor is self-describing, so a continuation read pages in the cursor's own direction regardless of the `direction` passed alongside it — a forward cursor always pages forward and a backward cursor always pages backward. Pass the direction that matches the cursor to avoid confusion. + #[serde(skip_serializing_if = "Option::is_none")] + pub direction: Option, + /// When false, skip ephemeral events entirely and return only durable (persisted) events. History-backfill callers that discard ephemerals anyway should set this so the read is bounded by the durable log length instead of racing the ephemeral ring on a busy session. Defaults to true (ephemerals are interleaved with durable events in creation order). Ignored by backward reads, which always cover persisted history only. #[serde(skip_serializing_if = "Option::is_none")] pub include_ephemeral: Option, /// Maximum number of events to return in this batch (1–1000, default 200). @@ -3450,7 +3572,7 @@ pub struct EventLogReadRequest { /// Either '*' to receive all event types, or a non-empty list of event types to receive #[serde(skip_serializing_if = "Option::is_none")] pub types: Option, - /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). + /// Milliseconds to wait for new events when the cursor is at the tail of history. 0 (default) returns immediately even if no events are available. Capped at 30000ms. Ephemeral events that arrive during the wait are delivered in this batch but are NOT replayable on a subsequent read (use a non-zero waitMs in your next call to capture future ephemerals as they happen). This applies to forward reads only: a backward read always returns immediately and ignores `waitMs`, because backward paging covers persisted history only while new events append at the tail (the opposite end from a backward page), so no blocking or ephemeral delivery can occur. #[serde(skip_serializing_if = "Option::is_none")] pub wait_ms: Option, } @@ -3496,13 +3618,13 @@ pub struct EventLogTailResult { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EventsReadResult { - /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. + /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). pub cursor: String, - /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. + /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history. For a forward read the fallback starts from the beginning of the remaining history; for a backward read it falls back to the tail (the newest window). Because the fallback page is a fresh boundary snapshot rather than a continuation of the requested cursor, it may overlap events the consumer has already rendered — a backward fallback to the tail in particular can repeat the newest window. On 'expired', consumers should reset or rebase their local pagination state (or deduplicate by event id) before continuing from the returned cursor rather than blindly appending/prepending the fallback page. pub cursor_status: EventsCursorStatus, - /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. + /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. pub events: Vec, - /// True when the read returned `max` events and more events are available immediately. When false, the next read with a non-zero `waitMs` will block until a new event arrives or the wait expires. + /// True when more events are available in the read's direction. For a forward read, true means the batch returned `max` events and more are available immediately. For a backward read, true means older persisted events remain before the returned window. pub has_more: bool, } @@ -6663,6 +6785,25 @@ pub struct McpListToolsResult { pub tools: Vec, } +/// Identifies the MCP server whose persisted OAuth credentials were updated. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct McpOauthAuthenticationStateChangedRequest { + /// Whether the target session must mint a session-scoped access token instead of reusing a shared access token persisted by another session. + #[serde(skip_serializing_if = "Option::is_none")] + pub refresh_session_token: Option, + /// Name of the MCP server whose OAuth credentials were updated. Omit only when the host cannot identify the server. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_name: Option, +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct McpOauthPendingRequestResponseToken { @@ -8631,6 +8772,24 @@ pub struct PermissionDecisionApproveForSessionApprovalExtensionManagement { pub operation: Option, } +/// Session-scoped factory approval, optionally narrowed by approval key. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionDecisionApproveForSessionApprovalFactory { + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + #[serde(skip_serializing_if = "Option::is_none")] + pub approval_key: Option, + /// Approval covering factory operations. + pub kind: PermissionDecisionApproveForSessionApprovalFactoryKind, +} + /// Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. /// ///
@@ -8802,6 +8961,24 @@ pub struct PermissionDecisionApproveForLocationApprovalExtensionManagement { pub operation: Option, } +/// Location-scoped factory approval, optionally narrowed by approval key. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionDecisionApproveForLocationApprovalFactory { + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + #[serde(skip_serializing_if = "Option::is_none")] + pub approval_key: Option, + /// Approval covering factory operations. + pub kind: PermissionDecisionApproveForLocationApprovalFactoryKind, +} + /// Location-scoped approval details for an extension's permission-gated capability access, keyed by extension name. /// ///
@@ -9200,6 +9377,24 @@ pub struct PermissionsLocationsAddToolApprovalDetailsExtensionManagement { pub operation: Option, } +/// Location-persisted factory approval, optionally narrowed by approval key. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionsLocationsAddToolApprovalDetailsFactory { + /// Optional factory operation name or canonical approval key; when omitted, the approval covers all factory operations. + #[serde(skip_serializing_if = "Option::is_none")] + pub approval_key: Option, + /// Approval covering factory operations. + pub kind: PermissionsLocationsAddToolApprovalDetailsFactoryKind, +} + /// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. /// ///
@@ -9802,7 +9997,7 @@ pub struct PermissionsSetAllowAllRequest { /// Allow-all mode to apply. `on` enables full allow-all; `auto` enables advisory LLM auto-approval; `off` disables both. #[serde(skip_serializing_if = "Option::is_none")] pub mode: Option, - /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session's active model is used. + /// Optional model id for the `auto` mode auto-approval LLM judging. Only meaningful when `mode` is `auto`; ignored otherwise. When omitted, the session resolves a default judge model: `gpt-5.5` for CAPI sessions and the session's active model for BYOK sessions. #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. @@ -12107,6 +12302,9 @@ pub struct SandboxConfig { /// Whether to auto-add the current working directory to readwritePaths. Default: true. #[serde(skip_serializing_if = "Option::is_none")] pub add_current_working_directory: Option, + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + #[serde(skip_serializing_if = "Option::is_none")] + pub allow_dev_tool_caches: Option, /// Whether sandboxing is enabled for the session. pub enabled: bool, /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). @@ -17991,6 +18189,23 @@ pub struct McpConfigListResult { pub servers: HashMap, } +/// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExtensionsDiscoverResult { + /// Discovered user and enabled installed-plugin extensions from persisted Copilot home state + pub extensions: Vec, + /// Effective extension loading mode. Defaults to load_and_augment when unset. + pub mode: DiscoveredExtensionMode, +} + /// Plugins installed in user/global state. /// ///
@@ -22616,13 +22831,13 @@ pub struct SessionQueueProcessParams { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionEventLogReadResult { - /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. + /// Opaque cursor for the next read. Pass back unchanged in the next read.cursor to continue from where this read left off. Always present, even when no events were returned. For a backward read this cursor pages toward OLDER events; keep passing `direction: backward` with it (the cursor is also self-describing, so backward paging continues correctly). pub cursor: String, - /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. + /// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history. For a forward read the fallback starts from the beginning of the remaining history; for a backward read it falls back to the tail (the newest window). Because the fallback page is a fresh boundary snapshot rather than a continuation of the requested cursor, it may overlap events the consumer has already rendered — a backward fallback to the tail in particular can repeat the newest window. On 'expired', consumers should reset or rebase their local pagination state (or deduplicate by event id) before continuing from the returned cursor rather than blindly appending/prepending the fallback page. pub cursor_status: EventsCursorStatus, - /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. + /// Session events for this batch, merged into a single stream in creation order: durable (persisted) events and ephemeral events interleave exactly as they were emitted. Set `includeEphemeral: false` to receive only durable events. Ephemeral events are never replayable once pruned from the in-memory ring, so a consumer that needs them should keep reading with a non-zero `waitMs`. For a backward (tail-first) read, the returned window contains persisted events only, still in chronological (oldest-to-newest) append order. pub events: Vec, - /// True when the read returned `max` events and more events are available immediately. When false, the next read with a non-zero `waitMs` will block until a new event arrives or the wait expires. + /// True when more events are available in the read's direction. For a forward read, true means the batch returned `max` events and more are available immediately. For a backward read, true means older persisted events remain before the returned window. pub has_more: bool, } @@ -23955,6 +24170,53 @@ pub enum DebugCollectLogsResultKind { Unknown, } +/// Persisted extension discovery source +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum DiscoveredExtensionSource { + /// Extension discovered from the user's extensions directory. + #[serde(rename = "user")] + User, + /// Extension contributed by an installed plugin. + #[serde(rename = "plugin")] + Plugin, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Effective extension loading and agent-management mode +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum DiscoveredExtensionMode { + /// Extensions are not loaded. + #[serde(rename = "disabled")] + Disabled, + /// Extensions are loaded, but the agent cannot create, reload, or manage them. + #[serde(rename = "load_only")] + LoadOnly, + /// Extensions are loaded and the agent can create, reload, and manage them. + #[serde(rename = "load_and_augment")] + LoadAndAugment, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Server transport type: stdio, http, sse (deprecated), or memory /// ///
@@ -24013,7 +24275,29 @@ pub enum EventsAgentScope { Unknown, } -/// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read started from the beginning of the remaining history. +/// Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum EventsReadDirection { + /// Page from the cursor toward newer events (default). + #[serde(rename = "forward")] + Forward, + /// Tail-first: return the newest events and page toward older events. + #[serde(rename = "backward")] + Backward, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Cursor status: 'ok' means the cursor was applied successfully; 'expired' means the cursor referred to an event that no longer exists in history (e.g. truncated or compacted away) and the read fell back to a boundary of the remaining history (the beginning for a forward read, the tail for a backward read). The fallback page is a fresh boundary snapshot, not a continuation of the requested cursor, so it may overlap already-rendered events; on 'expired' a consumer should reset/rebase its pagination state (or deduplicate by event id) before continuing from the returned cursor. /// ///
/// @@ -25601,6 +25885,14 @@ pub enum PermissionDecisionApproveForSessionApprovalExtensionManagementKind { ExtensionManagement, } +/// Approval covering factory operations. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionDecisionApproveForSessionApprovalFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + /// Approval covering an extension's request to access a permission-gated capability. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionDecisionApproveForSessionApprovalExtensionPermissionAccessKind { @@ -25628,6 +25920,7 @@ pub enum PermissionDecisionApproveForSessionApproval { Memory(PermissionDecisionApproveForSessionApprovalMemory), CustomTool(PermissionDecisionApproveForSessionApprovalCustomTool), ExtensionManagement(PermissionDecisionApproveForSessionApprovalExtensionManagement), + Factory(PermissionDecisionApproveForSessionApprovalFactory), ExtensionPermissionAccess(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess), } @@ -25703,6 +25996,14 @@ pub enum PermissionDecisionApproveForLocationApprovalExtensionManagementKind { ExtensionManagement, } +/// Approval covering factory operations. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionDecisionApproveForLocationApprovalFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + /// Approval covering an extension's request to access a permission-gated capability. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind { @@ -25730,6 +26031,7 @@ pub enum PermissionDecisionApproveForLocationApproval { Memory(PermissionDecisionApproveForLocationApprovalMemory), CustomTool(PermissionDecisionApproveForLocationApprovalCustomTool), ExtensionManagement(PermissionDecisionApproveForLocationApprovalExtensionManagement), + Factory(PermissionDecisionApproveForLocationApprovalFactory), ExtensionPermissionAccess( PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess, ), @@ -25933,6 +26235,14 @@ pub enum PermissionsLocationsAddToolApprovalDetailsExtensionManagementKind { ExtensionManagement, } +/// Approval covering factory operations. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionsLocationsAddToolApprovalDetailsFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + /// Approval covering an extension's request to access a permission-gated capability. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccessKind { @@ -25960,6 +26270,7 @@ pub enum PermissionsLocationsAddToolApprovalDetails { Memory(PermissionsLocationsAddToolApprovalDetailsMemory), CustomTool(PermissionsLocationsAddToolApprovalDetailsCustomTool), ExtensionManagement(PermissionsLocationsAddToolApprovalDetailsExtensionManagement), + Factory(PermissionsLocationsAddToolApprovalDetailsFactory), ExtensionPermissionAccess(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess), } diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index e9bbef1d1..3749b8947 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -50,6 +50,13 @@ impl<'a> ClientRpc<'a> { } } + /// `extensions.*` sub-namespace. + pub fn extensions(&self) -> ClientRpcExtensions<'a> { + ClientRpcExtensions { + client: self.client, + } + } + /// `instructions.*` sub-namespace. pub fn instructions(&self) -> ClientRpcInstructions<'a> { ClientRpcInstructions { @@ -496,6 +503,86 @@ impl<'a> ClientRpcCommands<'a> { } } +/// `extensions.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcExtensions<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcExtensions<'a> { + /// Discovers user and enabled installed-plugin extensions from persisted Copilot home state, including enablement preferences. Launch-scoped additional plugins are not included. + /// + /// Wire method: `extensions.discover`. + /// + /// # Returns + /// + /// Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn discover(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::EXTENSIONS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Persistently enables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.enable to update them. + /// + /// Wire method: `extensions.enable`. + /// + /// # Parameters + /// + /// * `params` - Source-qualified extension identifiers to persistently enable for future sessions. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn enable(&self, params: DiscoveredExtensionsEnableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::EXTENSIONS_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + + /// Persistently disables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.disable to update them. + /// + /// Wire method: `extensions.disable`. + /// + /// # Parameters + /// + /// * `params` - Source-qualified extension identifiers to persistently disable for future sessions. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn disable(&self, params: DiscoveredExtensionsDisableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::EXTENSIONS_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } +} + /// `instructions.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcInstructions<'a> { @@ -3298,6 +3385,32 @@ impl<'a> SessionRpcAgent<'a> { Ok(serde_json::from_value(_value)?) } + /// Sets an in-memory authored prompt override for an available agent. For built-in agents, this replaces only the static base prompt while preserving runtime-owned dynamic prompt composition and behavior. The special `general-purpose` agent is not overrideable. Overrides are not persisted; resumed and forked sessions start without them, so the host must re-apply them. + /// + /// Wire method: `session.agent.setPrompt`. + /// + /// # Parameters + /// + /// * `params` - An in-memory authored prompt override for an available agent. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn set_prompt(&self, params: AgentSetPromptRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_SETPROMPT, Some(wire_params)) + .await?; + Ok(()) + } + /// Gets the currently selected custom agent for the session. /// /// Wire method: `session.agent.getCurrent`. @@ -3962,7 +4075,7 @@ pub struct SessionRpcEventLog<'a> { } impl<'a> SessionRpcEventLog<'a> { - /// Reads a batch of session events from a cursor, optionally waiting for new events. + /// Reads a batch of session events from a cursor, optionally waiting for new events. Supports tail-first reads via `direction: backward`. /// /// Wire method: `session.eventLog.read`. /// @@ -5943,6 +6056,38 @@ impl<'a> SessionRpcMcpOauth<'a> { Ok(serde_json::from_value(_value)?) } + /// Notifies the session that MCP OAuth authentication succeeded and updated credentials were persisted, so cached tool definitions can be refreshed. + /// + /// Wire method: `session.mcp.oauth.authenticationStateChanged`. + /// + /// # Parameters + /// + /// * `params` - Identifies the MCP server whose persisted OAuth credentials were updated. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn authentication_state_changed( + &self, + params: McpOauthAuthenticationStateChangedRequest, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_OAUTH_AUTHENTICATIONSTATECHANGED, + Some(wire_params), + ) + .await?; + Ok(()) + } + /// Starts OAuth authentication for a remote MCP server. /// /// Wire method: `session.mcp.oauth.login`. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 7bcba377a..7ebaa08da 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -687,6 +687,24 @@ pub struct WorkingDirectoryContext { pub repository_host: Option, } +/// Per-session configuration for the built-in GitHub MCP server +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GitHubMcpToolConfig { + /// Additional GitHub MCP tools requested by the session + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_tools: Option>, + /// Additional GitHub MCP toolsets requested by the session + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_toolsets: Option>, + /// Whether to use the read-write endpoint and request all toolsets + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_all_tools: Option, + /// Whether to request the GitHub MCP insiders build + #[serde(skip_serializing_if = "Option::is_none")] + pub enable_insiders_mode: Option, +} + /// Optional session limits. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -714,6 +732,9 @@ pub struct SessionStartData { /// When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. #[serde(skip_serializing_if = "Option::is_none")] pub detached_from_spawning_parent_session_id: Option, + /// Per-session GitHub MCP override persisted for cold resume + #[serde(skip_serializing_if = "Option::is_none")] + pub github_mcp_tool_config: Option, /// Identifier of the software producing the events (e.g., "copilot-agent") pub producer: String, /// Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") @@ -1796,6 +1817,12 @@ pub struct AssistantMessageData { /// Provider's completion / response identifier; shared across all chunks of a single API call. Used to group multi-chunk assistant utterances. #[serde(skip_serializing_if = "Option::is_none")] pub api_call_id: Option, + /// Total messages the model call's response was split into, one per reasoning boundary. Absent for a single-message response; the last chunk is the one where chunkIndex is chunkCount - 1. + #[serde(skip_serializing_if = "Option::is_none")] + pub chunk_count: Option, + /// Zero-based position of this message within its model call's response. Absent when the response was not split into chunks. + #[serde(skip_serializing_if = "Option::is_none")] + pub chunk_index: Option, /// Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. /// ///
@@ -3264,6 +3291,63 @@ pub struct PermissionRequestExtensionManagement { pub tool_call_id: Option, } +/// A declared phase shown in a factory permission prompt. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FactoryPermissionPhase { + /// Optional phase detail + #[serde(skip_serializing_if = "Option::is_none")] + pub detail: Option, + /// Phase title + pub title: String, +} + +/// Factory run or authoring permission request +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionRequestFactory { + /// Canonical key used for scoped factory approvals + pub approval_key: String, + /// Whether this factory is eligible for persistent approval + pub can_persist_approval: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_ai_credits: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_concurrent_subagents: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_total_subagents: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_timeout_seconds: Option, + /// Factory description + pub description: String, + /// Permission kind discriminator + pub kind: PermissionRequestFactoryKind, + /// When true, managed policy requires an explicit user decision and automatic approval must be bypassed. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, + /// Effective AI-credit limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_ai_credits: Option, + /// Effective concurrent-subagent limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_concurrent_subagents: Option, + /// Effective total-subagent limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_total_subagents: Option, + /// Factory name + pub name: String, + /// Factory operation, either run or author + pub operation: FactoryPermissionOperation, + /// Declared factory phases + pub phases: Vec, + /// Effective active-time limit in seconds; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout_seconds: Option, + /// Tool call ID that triggered this permission request + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_call_id: Option, +} + /// Extension permission access request #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -3617,6 +3701,62 @@ pub struct PermissionPromptRequestExtensionManagement { pub tool_call_id: Option, } +/// Factory run or authoring permission prompt +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionPromptRequestFactory { + /// Canonical key used for scoped factory approvals + pub approval_key: String, + /// Auto-approval judge information for this request; present only when auto mode is enabled. + /// + ///
+ /// + /// **Experimental.** This type is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. + /// + ///
+ #[serde(skip_serializing_if = "Option::is_none")] + pub auto_approval: Option, + /// Whether this factory is eligible for persistent approval + pub can_persist_approval: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_ai_credits: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_concurrent_subagents: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_max_total_subagents: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub declared_timeout_seconds: Option, + /// Factory description + pub description: String, + /// Prompt kind discriminator + pub kind: PermissionPromptRequestFactoryKind, + /// Whether managed policy requires a human response and forbids host auto-approval + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_approval_required: Option, + /// Effective AI-credit limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_ai_credits: Option, + /// Effective concurrent-subagent limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_concurrent_subagents: Option, + /// Effective total-subagent limit; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub max_total_subagents: Option, + /// Factory name + pub name: String, + /// Factory operation, either run or author + pub operation: FactoryPermissionOperation, + /// Declared factory phases + pub phases: Vec, + /// Effective active-time limit in seconds; omitted means unlimited + #[serde(skip_serializing_if = "Option::is_none")] + pub timeout_seconds: Option, + /// Tool call ID that triggered this permission request + #[serde(skip_serializing_if = "Option::is_none")] + pub tool_call_id: Option, +} + /// Extension permission access prompt #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -3736,6 +3876,17 @@ pub struct UserToolSessionApprovalExtensionManagement { pub operation: Option, } +/// Session-scoped factory approval, optionally narrowed by approval key. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserToolSessionApprovalFactory { + /// Optional factory operation name or canonical approval key + #[serde(skip_serializing_if = "Option::is_none")] + pub approval_key: Option, + /// Factory approval kind + pub kind: UserToolSessionApprovalFactoryKind, +} + /// Session-scoped tool-approval rule for an extension's permission-gated capability access, keyed by extension name. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -5576,6 +5727,29 @@ pub enum PermissionRequestExtensionManagementKind { ExtensionManagement, } +/// Permission kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionRequestFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + +/// Operation gated by a factory permission request. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum FactoryPermissionOperation { + /// Running a registered factory, which spends subagents, active time, and AI credits under the approved limits. + #[serde(rename = "run")] + Run, + /// Authoring a factory, which writes JavaScript into a session-scoped extension and loads it. + #[serde(rename = "author")] + Author, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Permission kind discriminator #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionRequestExtensionPermissionAccessKind { @@ -5597,6 +5771,7 @@ pub enum PermissionRequest { CustomTool(PermissionRequestCustomTool), Hook(PermissionRequestHook), ExtensionManagement(PermissionRequestExtensionManagement), + Factory(PermissionRequestFactory), ExtensionPermissionAccess(PermissionRequestExtensionPermissionAccess), } @@ -5757,6 +5932,14 @@ pub enum PermissionPromptRequestExtensionManagementKind { ExtensionManagement, } +/// Prompt kind discriminator +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionPromptRequestFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + /// Prompt kind discriminator #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionPromptRequestExtensionPermissionAccessKind { @@ -5779,6 +5962,7 @@ pub enum PermissionPromptRequest { Path(PermissionPromptRequestPath), Hook(PermissionPromptRequestHook), ExtensionManagement(PermissionPromptRequestExtensionManagement), + Factory(PermissionPromptRequestFactory), ExtensionPermissionAccess(PermissionPromptRequestExtensionPermissionAccess), } @@ -5846,6 +6030,14 @@ pub enum UserToolSessionApprovalExtensionManagementKind { ExtensionManagement, } +/// Factory approval kind +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum UserToolSessionApprovalFactoryKind { + #[serde(rename = "factory")] + #[default] + Factory, +} + /// Extension permission access approval kind #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum UserToolSessionApprovalExtensionPermissionAccessKind { @@ -5865,6 +6057,7 @@ pub enum UserToolSessionApproval { Memory(UserToolSessionApprovalMemory), CustomTool(UserToolSessionApprovalCustomTool), ExtensionManagement(UserToolSessionApprovalExtensionManagement), + Factory(UserToolSessionApprovalFactory), ExtensionPermissionAccess(UserToolSessionApprovalExtensionPermissionAccess), } diff --git a/rust/tests/e2e/rpc_event_log.rs b/rust/tests/e2e/rpc_event_log.rs index 18122337a..84d575ee3 100644 --- a/rust/tests/e2e/rpc_event_log.rs +++ b/rust/tests/e2e/rpc_event_log.rs @@ -43,8 +43,10 @@ async fn should_read_persisted_events_from_beginning() { .rpc() .event_log() .read(EventLogReadRequest { + agent_ids: None, agent_scope: None, cursor: None, + direction: None, include_ephemeral: None, max: Some(100), types: Some(json!("*")), @@ -89,8 +91,10 @@ async fn should_return_tail_cursor_and_read_empty_when_no_new_events() { .rpc() .event_log() .read(EventLogReadRequest { + agent_ids: None, agent_scope: None, cursor: Some(tail.cursor), + direction: None, include_ephemeral: None, max: Some(10), types: Some(json!("*")), @@ -172,8 +176,10 @@ async fn should_longpoll_with_types_filter_for_titlechanged_event() { let tail = session.rpc().event_log().tail().await.expect("tail"); let event_log = session.rpc().event_log(); let read_future = event_log.read(EventLogReadRequest { + agent_ids: None, agent_scope: None, cursor: Some(tail.cursor), + direction: None, include_ephemeral: None, max: Some(10), types: Some(json!(["session.title_changed"])), diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 97fcebea6..2d68e68e2 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -70,6 +70,51 @@ const POLYMORPHIC_BASE_PROPERTIES: Record = { PermissionRequest: ["managedApprovalRequired"], }; +/** + * Public type names declared by hand-written C# sources under `dotnet/src` + * (excluding `dotnet/src/Generated`). Generated session-event types share the + * `GitHub.Copilot` namespace with those sources, so a schema definition whose + * name collides with a hand-written declaration must reuse it — emitting a + * second class of the same name fails the build (CS0260/CS0102). + * + * Populated by {@link collectHandWrittenCSharpTypeNames} before generation. + */ +let handWrittenCSharpTypeNames = new Set(); + +/** + * Scan hand-written `.cs` files under `dotnet/src` for top-level public type + * declarations. The `Generated` directory is skipped so this scanner never + * reads (or depends on the output of) its own emit. + */ +async function collectHandWrittenCSharpTypeNames(): Promise> { + const names = new Set(); + const srcDir = path.join(REPO_ROOT, "dotnet", "src"); + const declaration = /^\s*(?:public|internal)\s+(?:(?:abstract|sealed|static|partial|readonly|ref)\s+)*(?:class|record|struct|interface|enum)\s+([A-Za-z_]\w*)/gm; + + const walk = async (dir: string): Promise => { + let entries; + try { + entries = await fs.readdir(dir, { withFileTypes: true }); + } catch { + return; + } + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === "Generated" || entry.name === "bin" || entry.name === "obj") continue; + await walk(entryPath); + continue; + } + if (!entry.name.endsWith(".cs")) continue; + const content = await fs.readFile(entryPath, "utf-8"); + for (const match of content.matchAll(declaration)) names.add(match[1]); + } + }; + + await walk(srcDir); + return names; +} + /** Apply rename to a generated class name, checking both exact match and prefix replacement for derived types. */ function applyTypeRename(className: string): string { if (TYPE_RENAMES[className]) return TYPE_RENAMES[className]; @@ -1456,8 +1501,13 @@ namespace GitHub.Copilot; lines.push(generateDataClass(variant, knownTypes, nestedClasses, enumOutput), ""); } - // Nested classes - for (const [, code] of nestedClasses) lines.push(code, ""); + // Nested classes. A name already declared by a hand-written source is skipped: + // that declaration is the one the namespace keeps, and the generated property + // simply binds to it. + for (const [name, code] of nestedClasses) { + if (handWrittenCSharpTypeNames.has(name)) continue; + lines.push(code, ""); + } // Enums for (const code of enumOutput) lines.push(code); @@ -1477,6 +1527,7 @@ export async function generateSessionEvents(schemaPath?: string): Promise const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath()); const schema = cloneSchemaForCodegen((await loadSchemaJson(resolvedPath)) as JSONSchema7); const processed = propagateInternalVisibility(postProcessSchema(schema)); + handWrittenCSharpTypeNames = await collectHandWrittenCSharpTypeNames(); const code = generateSessionEventsCode(processed); const outPath = await writeGeneratedFile("dotnet/src/Generated/SessionEvents.cs", code); console.log(` ✓ ${outPath}`); @@ -2629,6 +2680,7 @@ namespace GitHub.Copilot.Rpc; export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema7): Promise { console.log("C#: generating RPC types..."); const resolvedPath = schemaPath ?? (await getApiSchemaPath()); + handWrittenCSharpTypeNames = await collectHandWrittenCSharpTypeNames(); let schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen((await loadSchemaJson(resolvedPath)) as ApiSchema)); if (sessionEventsSchema) { const sharedDefinitions = findSharedSchemaDefinitions( @@ -2658,7 +2710,9 @@ export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSO for (const name of reachableDefinitions) { const typeName = typeToClassName(name); const declarationPattern = new RegExp(`\\bpublic\\s+(?:(?:sealed|abstract|partial|readonly)\\s+)*(?:class|struct)\\s+${typeName}\\b`); - if (declarationPattern.test(sessionEventsCode)) { + // A hand-written declaration also lives in `GitHub.Copilot`, so the + // reference resolves even though the generated file skipped it. + if (declarationPattern.test(sessionEventsCode) || handWrittenCSharpTypeNames.has(typeName)) { emittedDefinitions.add(name); } const valueTypeDeclarationPattern = new RegExp(`\\bpublic\\s+(?:(?:readonly)\\s+)?struct\\s+${typeName}\\b`); diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index ba4c9442b..42e78b9a0 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -457,6 +457,7 @@ const PERMISSION_REQUEST_DEFINITION_NAMES = [ "PermissionRequestCustomTool", "PermissionRequestExtensionManagement", "PermissionRequestExtensionPermissionAccess", + "PermissionRequestFactory", "PermissionRequestHook", "PermissionRequestMcp", "PermissionRequestMemory", diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 76b19f6ad..4cfbf7b85 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.77.tgz", - "integrity": "sha512-nkTtDPKvsClAByPPqnD/57vK7YIBK1dgiv7aVc9uO3rxKCyqiqYaBqwi8pMzesvGP3yl+//+iMzaBXNWEcZVWQ==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", + "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.77", - "@github/copilot-darwin-x64": "1.0.77", - "@github/copilot-linux-arm64": "1.0.77", - "@github/copilot-linux-x64": "1.0.77", - "@github/copilot-linuxmusl-arm64": "1.0.77", - "@github/copilot-linuxmusl-x64": "1.0.77", - "@github/copilot-win32-arm64": "1.0.77", - "@github/copilot-win32-x64": "1.0.77" + "@github/copilot-darwin-arm64": "1.0.78-2", + "@github/copilot-darwin-x64": "1.0.78-2", + "@github/copilot-linux-arm64": "1.0.78-2", + "@github/copilot-linux-x64": "1.0.78-2", + "@github/copilot-linuxmusl-arm64": "1.0.78-2", + "@github/copilot-linuxmusl-x64": "1.0.78-2", + "@github/copilot-win32-arm64": "1.0.78-2", + "@github/copilot-win32-x64": "1.0.78-2" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.77.tgz", - "integrity": "sha512-sCWSH5+Flm/OxFe7dzsBfyj7ADBkzkR54Sz5NGw7dtcVVEOnVUkZLjEtNmZ1t5QRD4Sf1+g/DiwgJEbsR9xR1w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", + "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.77.tgz", - "integrity": "sha512-ReNlB+g+OBiqHwmY5leJBIyvHZQcjyWL/OY8aVimHyESn2ToPKP3eUNTzSUJvvbPM6+0LXwEpijLedkRd2Cn1g==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", + "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.77.tgz", - "integrity": "sha512-A8j/WBPFvV5WfLbgnIIQLUVuFRAR7kLyc5WgId6XLCu1ARbkRM7353zz9mEXXwjc6LqotHVg80ooANJjNtmSPg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", + "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.77.tgz", - "integrity": "sha512-2eefKkdUnQ1Y8oxyRyexHBXVpuSmrfEM8XJauquVjPc0JqF5nab9axwpFPzrRSF1GB+25F9tUK2sDQRyp08wag==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", + "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.77.tgz", - "integrity": "sha512-YtltOZQp8plytSKSGTWWKbOx3QD8iZH04sLtKTrYs6nu5UalIgFPoMkwamy1gh7h5EBkeVxD2s3epVrlvP4X4w==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", + "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.77.tgz", - "integrity": "sha512-owINwPgHU/ZZBwFhVPgkgGjLkF6e4QbdofADvKMdKJWV2+7oWjXUIlPA4/PwraD2Gkuu583l7m0XLL27TN8oUA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", + "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.77.tgz", - "integrity": "sha512-l5oQaMLCRup0nmmpbqOAYEAJ5YWgNlaoO0psNaKDzvTbdzEJRZqib2t7+p3bgoDpK7SB/m8m1uxFC4XT3hlprg==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", + "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.77", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.77.tgz", - "integrity": "sha512-8Mo9y3/8CVU2w35WqwSiRMTGH1kKHR3URPSJYF4J4OG8L7NOEy2fafXR9Tuq3H21Srg3OzFkl/A+Taunqz9KcA==", + "version": "1.0.78-2", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", + "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 07b82a4d6..df0a1b968 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.77", + "@github/copilot": "^1.0.78-2", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From 26b88ce89e0987d47c6d46fae880d420834459a0 Mon Sep 17 00:00:00 2001 From: Rince Yuan Date: Mon, 3 Aug 2026 21:56:12 +0800 Subject: [PATCH 07/72] fix(python): serialize Pydantic models with mode='json' in tool results (#2225) * fix(python): serialize Pydantic models with mode='json' in tool results model_dump() defaults to mode='python', which leaves datetime, UUID, Decimal, Enum and set as native Python objects. json.dumps then fails on these, and the error is swallowed as a generic tool failure. Use model_dump(mode='json') so Pydantic converts all fields to JSON-safe types before serialization. Closes #2203 * test(python): strengthen Pydantic result coverage Assert the JSON representation of date and datetime fields, cover set serialization, and fix import ordering for Ruff. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: j-zhangyiyuan Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/tools.py | 2 +- python/test_tools.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/python/copilot/tools.py b/python/copilot/tools.py index 762b79c45..de81fe7fd 100644 --- a/python/copilot/tools.py +++ b/python/copilot/tools.py @@ -351,7 +351,7 @@ def _normalize_result(result: Any) -> ToolResult: # Everything else gets JSON-serialized (with Pydantic model support) def default(obj: Any) -> Any: if isinstance(obj, BaseModel): - return obj.model_dump() + return obj.model_dump(mode="json") raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable") try: diff --git a/python/test_tools.py b/python/test_tools.py index 646f17c03..97de41df4 100644 --- a/python/test_tools.py +++ b/python/test_tools.py @@ -389,6 +389,44 @@ class Item(BaseModel): assert parsed == [{"name": "a", "value": 1}, {"name": "b", "value": 2}] assert result.result_type == "success" + def test_pydantic_model_with_non_primitive_fields_is_serialized(self): + from datetime import date, datetime + from decimal import Decimal + from enum import Enum + from uuid import UUID + + class Status(Enum): + ACTIVE = "active" + + class Record(BaseModel): + id: UUID + created: datetime + day: date + score: Decimal + status: Status + tags: set[str] + + record = Record( + id=UUID("12345678-1234-5678-1234-567812345678"), + created=datetime(2026, 1, 15, 10, 30, 0), + day=date(2026, 1, 15), + score=Decimal("99.5"), + status=Status.ACTIVE, + tags={"python", "sdk"}, + ) + result = _normalize_result(record) + parsed = json.loads(result.text_result_for_llm) + assert parsed == { + "id": "12345678-1234-5678-1234-567812345678", + "created": "2026-01-15T10:30:00", + "day": "2026-01-15", + "score": "99.5", + "status": "active", + "tags": parsed["tags"], + } + assert set(parsed["tags"]) == {"python", "sdk"} + assert result.result_type == "success" + def test_raises_for_unserializable_value(self): # Functions cannot be JSON serialized with pytest.raises(TypeError, match="Failed to serialize"): From eea0c980355b7dc3d81c97abf34ef2e9b00bee2d Mon Sep 17 00:00:00 2001 From: Jeremy Moseley Date: Mon, 3 Aug 2026 07:51:18 -0700 Subject: [PATCH 08/72] Add EnableExperimentalMode support across all 6 SDKs (#1600) Adds per-session EnableExperimentalMode (enableExperimentalMode / enable_experimental_mode) to all SDK languages. The flag controls whether the session enables experimental features. Semantics are mode-aware and consistent across languages: - In "empty" mode the SDK sends false unless the caller explicitly sets true, so headless integrations are not silently opted into experimental behaviour. - In "copilot-cli" mode the field is omitted from the wire when nil/null/ None, letting the runtime decide (e.g. based on staff-user flags). Wire field is isExperimentalMode on both session.create and session.resume. Changes per language: - Rust: enable_experimental_mode on SessionConfig / ResumeSessionConfig, experimental_mode_for_mode helper in mode.rs, 6 new unit tests - .NET: EnableExperimentalMode on SessionOptions / SessionResumeOptions, mode-aware default in Client.CreateSessionAsync/ResumeSessionAsync - Node: enableExperimentalMode on SessionOptions / SessionResumeOptions - Python: enable_experimental_mode param on create_session/resume_session, _enable_experimental_mode_default helper in _mode.py - Go: EnableExperimentalMode on SessionConfig / ResumeSessionConfig, empty-mode default in mode_empty.go - Java: enableExperimentalMode on SessionConfig / ResumeSessionConfig, experimentalModeForMode helper in SessionRequestBuilder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 5 + dotnet/src/Types.cs | 10 ++ dotnet/test/Unit/CloneTests.cs | 35 ++++++ dotnet/test/Unit/GitHubTelemetryTests.cs | 45 ++++++++ dotnet/test/Unit/SerializationTests.cs | 34 ++++++ go/client.go | 2 + go/client_test.go | 57 ++++++++++ go/mode_empty.go | 8 ++ go/toolset_test.go | 36 ++++++ go/types.go | 10 ++ .../github/copilot/SessionRequestBuilder.java | 12 ++ .../copilot/rpc/CreateSessionRequest.java | 28 +++++ .../copilot/rpc/ResumeSessionConfig.java | 48 ++++++++ .../copilot/rpc/ResumeSessionRequest.java | 28 +++++ .../com/github/copilot/rpc/SessionConfig.java | 48 ++++++++ .../copilot/SessionRequestBuilderTest.java | 41 +++++++ nodejs/src/client.ts | 7 ++ nodejs/src/types.ts | 6 + nodejs/test/client.test.ts | 97 +++++++++++++++++ python/copilot/_mode.py | 8 ++ python/copilot/client.py | 15 +++ python/test_client.py | 103 ++++++++++++++++++ rust/src/mode.rs | 33 ++++++ rust/src/session.rs | 4 + rust/src/types.rs | 85 +++++++++++++++ rust/src/wire.rs | 4 + 26 files changed, 809 insertions(+) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 46ce7ba80..9b50d6942 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -908,6 +908,7 @@ private void ApplyConfigDefaultsForMode(SessionConfigBase config) { if (_options.Mode == CopilotClientMode.Empty) { + config.EnableExperimentalMode ??= false; config.EnableSessionTelemetry ??= false; config.SkipEmbeddingRetrieval ??= true; config.EmbeddingCacheStorage ??= EmbeddingCacheStorageMode.InMemory; @@ -1146,6 +1147,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.Provider, config.Capi, config.EnableSessionTelemetry, + config.EnableExperimentalMode, config.OnPermissionRequest != null ? true : null, config.OnUserInputRequest != null ? true : null, config.OnExitPlanModeRequest != null ? true : null, @@ -1360,6 +1362,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.Provider, config.Capi, config.EnableSessionTelemetry, + config.EnableExperimentalMode, config.OnPermissionRequest != null ? true : null, config.OnUserInputRequest != null ? true : null, config.OnExitPlanModeRequest != null ? true : null, @@ -2715,6 +2718,7 @@ internal record CreateSessionRequest( ProviderConfig? Provider, CapiSessionOptions? Capi, bool? EnableSessionTelemetry, + bool? IsExperimentalMode, bool? RequestPermission, bool? RequestUserInput, bool? RequestExitPlanMode, @@ -2821,6 +2825,7 @@ internal record ResumeSessionRequest( ProviderConfig? Provider, CapiSessionOptions? Capi, bool? EnableSessionTelemetry, + bool? IsExperimentalMode, bool? RequestPermission, bool? RequestUserInput, bool? RequestExitPlanMode, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 6b511117c..e0b3d1914 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3068,6 +3068,7 @@ protected SessionConfigBase(SessionConfigBase? other) Providers = other.Providers is not null ? [.. other.Providers] : null; Models = other.Models is not null ? [.. other.Models] : null; EnableSessionTelemetry = other.EnableSessionTelemetry; + EnableExperimentalMode = other.EnableExperimentalMode; SkipCustomInstructions = other.SkipCustomInstructions; CustomAgentsLocalOnly = other.CustomAgentsLocalOnly; CoauthorEnabled = other.CoauthorEnabled; @@ -3270,6 +3271,15 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableSessionTelemetry { get; set; } + /// + /// Controls whether the session enables experimental features. + /// + /// + /// Defaults to in . + /// Otherwise, the runtime decides when left . + /// + public bool? EnableExperimentalMode { get; set; } + /// /// When , suppresses loading of custom instruction files /// (e.g. .github/copilot-instructions.md, AGENTS.md) from the working directory. diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index ec509ab16..e14003694 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -78,6 +78,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Streaming = true, EnableCitations = true, EnableSessionTelemetry = false, + EnableExperimentalMode = true, EnableOnDemandInstructionDiscovery = true, IncludeSubAgentStreamingEvents = false, McpServers = new Dictionary { ["server1"] = new McpStdioServerConfig { Command = "echo" } }, @@ -122,6 +123,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Assert.Equal(original.Streaming, clone.Streaming); Assert.Equal(original.EnableCitations, clone.EnableCitations); Assert.Equal(original.EnableSessionTelemetry, clone.EnableSessionTelemetry); + Assert.Equal(original.EnableExperimentalMode, clone.EnableExperimentalMode); Assert.Equal(original.EnableOnDemandInstructionDiscovery, clone.EnableOnDemandInstructionDiscovery); Assert.Equal(original.IncludeSubAgentStreamingEvents, clone.IncludeSubAgentStreamingEvents); Assert.Equal(original.McpServers.Count, clone.McpServers!.Count); @@ -373,6 +375,19 @@ public void ResumeSessionConfig_Clone_CopiesEnableSessionTelemetry() Assert.False(clone.EnableSessionTelemetry); } + [Fact] + public void ResumeSessionConfig_Clone_CopiesEnableExperimentalMode() + { + var original = new ResumeSessionConfig + { + EnableExperimentalMode = true, + }; + + var clone = original.Clone(); + + Assert.True(clone.EnableExperimentalMode); + } + [Fact] public void ResumeSessionConfig_Clone_CopiesContinuePendingWork() { @@ -460,6 +475,26 @@ public void ResumeSessionConfig_Clone_PreservesEnableSessionTelemetryDefault() Assert.Null(clone.EnableSessionTelemetry); } + [Fact] + public void SessionConfig_Clone_PreservesEnableExperimentalModeDefault() + { + var original = new SessionConfig(); + + var clone = original.Clone(); + + Assert.Null(clone.EnableExperimentalMode); + } + + [Fact] + public void ResumeSessionConfig_Clone_PreservesEnableExperimentalModeDefault() + { + var original = new ResumeSessionConfig(); + + var clone = original.Clone(); + + Assert.Null(clone.EnableExperimentalMode); + } + [Fact] public void SessionConfig_Clone_CopiesEnableOnDemandInstructionDiscovery() { diff --git a/dotnet/test/Unit/GitHubTelemetryTests.cs b/dotnet/test/Unit/GitHubTelemetryTests.cs index f82e0db6e..24e633387 100644 --- a/dotnet/test/Unit/GitHubTelemetryTests.cs +++ b/dotnet/test/Unit/GitHubTelemetryTests.cs @@ -193,6 +193,50 @@ await server.SendGitHubTelemetryEventAsync(new Dictionary Assert.Equal(false, clientInfo.IsStaff); } + [Fact] + public async Task CreateSession_EmptyMode_Sends_IsExperimentalMode_False_By_Default() + { + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + Mode = CopilotClientMode.Empty, + BaseDirectory = Path.GetTempPath(), + }); + await client.StartAsync(); + + await client.CreateSessionAsync(new SessionConfig + { + AvailableTools = new ToolSet().AddBuiltIn(BuiltInTools.Isolated).ToList(), + }); + + var createParams = server.LastCreateParams ?? throw new InvalidOperationException("session.create was not captured."); + Assert.True(createParams.TryGetProperty("isExperimentalMode", out var flag)); + Assert.False(flag.GetBoolean()); + } + + [Fact] + public async Task ResumeSession_EmptyMode_Sends_IsExperimentalMode_False_By_Default() + { + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + Mode = CopilotClientMode.Empty, + BaseDirectory = Path.GetTempPath(), + }); + await client.StartAsync(); + + await client.ResumeSessionAsync("session-1", new ResumeSessionConfig + { + AvailableTools = new ToolSet().AddBuiltIn(BuiltInTools.Isolated).ToList(), + }); + + var resumeParams = server.LastResumeParams ?? throw new InvalidOperationException("session.resume was not captured."); + Assert.True(resumeParams.TryGetProperty("isExperimentalMode", out var flag)); + Assert.False(flag.GetBoolean()); + } + private sealed class FakeTelemetryServer : IAsyncDisposable { private readonly TcpListener _listener; @@ -307,6 +351,7 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel "session.resume" => CaptureResume(request), "session.send" => new Dictionary { ["messageId"] = "message-1" }, "session.destroy" => new Dictionary(), + "session.options.update" => new Dictionary { ["success"] = true }, "runtime.shutdown" => new Dictionary(), _ => throw new InvalidOperationException($"Unexpected RPC method '{method}'."), }; diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index 9108a8134..acad44f19 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -706,6 +706,40 @@ public void ResumeSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptio Assert.False(root.GetProperty("enableSessionTelemetry").GetBoolean()); } + [Fact] + public void SessionRequests_CanSerializeEnableExperimentalMode_WithSdkOptions() + { + var options = GetSerializerOptions(); + + var createRequestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest"); + var createRequest = CreateInternalRequest( + createRequestType, + ("SessionId", "session-id"), + ("IsExperimentalMode", false)); + var createRoot = JsonDocument.Parse(JsonSerializer.Serialize(createRequest, createRequestType, options)).RootElement; + Assert.False(createRoot.GetProperty("isExperimentalMode").GetBoolean()); + + var createRequestOmitted = CreateInternalRequest( + createRequestType, + ("SessionId", "session-id")); + var createOmittedRoot = JsonDocument.Parse(JsonSerializer.Serialize(createRequestOmitted, createRequestType, options)).RootElement; + Assert.False(createOmittedRoot.TryGetProperty("isExperimentalMode", out _)); + + var resumeRequestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest"); + var resumeRequest = CreateInternalRequest( + resumeRequestType, + ("SessionId", "session-id"), + ("IsExperimentalMode", true)); + var resumeRoot = JsonDocument.Parse(JsonSerializer.Serialize(resumeRequest, resumeRequestType, options)).RootElement; + Assert.True(resumeRoot.GetProperty("isExperimentalMode").GetBoolean()); + + var resumeRequestOmitted = CreateInternalRequest( + resumeRequestType, + ("SessionId", "session-id")); + var resumeOmittedRoot = JsonDocument.Parse(JsonSerializer.Serialize(resumeRequestOmitted, resumeRequestType, options)).RootElement; + Assert.False(resumeOmittedRoot.TryGetProperty("isExperimentalMode", out _)); + } + [Fact] public void CreateSessionRequest_CanSerializeEnableOnDemandInstructionDiscovery_WithSdkOptions() { diff --git a/go/client.go b/go/client.go index 292a5729e..34f6d574a 100644 --- a/go/client.go +++ b/go/client.go @@ -796,6 +796,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.EnableSessionTelemetry = config.EnableSessionTelemetry req.EnableCitations = config.EnableCitations req.SessionLimits = config.SessionLimits + req.IsExperimentalMode = config.EnableExperimentalMode req.SkipCustomInstructions = config.SkipCustomInstructions req.CustomAgentsLocalOnly = config.CustomAgentsLocalOnly req.CoauthorEnabled = config.CoauthorEnabled @@ -1122,6 +1123,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.Providers = config.Providers req.Models = config.Models req.EnableSessionTelemetry = config.EnableSessionTelemetry + req.IsExperimentalMode = config.EnableExperimentalMode req.SkipCustomInstructions = config.SkipCustomInstructions req.CustomAgentsLocalOnly = config.CustomAgentsLocalOnly req.CoauthorEnabled = config.CoauthorEnabled diff --git a/go/client_test.go b/go/client_test.go index 2301c990d..2a6db6e86 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -2333,6 +2333,63 @@ func TestCreateSessionRequest_RequestMCPApps(t *testing.T) { }) } +func TestSessionRequests_EnableExperimentalMode(t *testing.T) { + t.Run("create forwards enableExperimentalMode when explicitly false", func(t *testing.T) { + req := createSessionRequest{ + IsExperimentalMode: Bool(false), + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["isExperimentalMode"] != false { + t.Errorf("Expected isExperimentalMode to be false, got %v", m["isExperimentalMode"]) + } + }) + + t.Run("create omits enableExperimentalMode when unset", func(t *testing.T) { + req := createSessionRequest{} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["isExperimentalMode"]; ok { + t.Error("Expected isExperimentalMode to be omitted when not set") + } + }) + + t.Run("resume forwards enableExperimentalMode when explicitly true", func(t *testing.T) { + req := resumeSessionRequest{ + SessionID: "s1", + IsExperimentalMode: Bool(true), + } + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["isExperimentalMode"] != true { + t.Errorf("Expected isExperimentalMode to be true, got %v", m["isExperimentalMode"]) + } + }) + + t.Run("resume omits enableExperimentalMode when unset", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1"} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["isExperimentalMode"]; ok { + t.Error("Expected isExperimentalMode to be omitted when not set") + } + }) +} + func TestResumeSessionRequest_RequestMCPApps(t *testing.T) { t.Run("sends requestMcpApps flag when EnableMCPApps is set", func(t *testing.T) { req := resumeSessionRequest{ diff --git a/go/mode_empty.go b/go/mode_empty.go index 46b8affce..6057b2661 100644 --- a/go/mode_empty.go +++ b/go/mode_empty.go @@ -122,6 +122,10 @@ func (c *Client) applyConfigDefaultsForMode(config *SessionConfig) { if c.options.Mode != ModeEmpty { return } + if config.EnableExperimentalMode == nil { + f := false + config.EnableExperimentalMode = &f + } if config.EnableSessionTelemetry == nil { f := false config.EnableSessionTelemetry = &f @@ -170,6 +174,10 @@ func (c *Client) applyResumeDefaultsForMode(config *ResumeSessionConfig) { if c.options.Mode != ModeEmpty { return } + if config.EnableExperimentalMode == nil { + f := false + config.EnableExperimentalMode = &f + } if config.EnableSessionTelemetry == nil { f := false config.EnableSessionTelemetry = &f diff --git a/go/toolset_test.go b/go/toolset_test.go index f269ffdfe..270d5b757 100644 --- a/go/toolset_test.go +++ b/go/toolset_test.go @@ -229,6 +229,24 @@ func TestApplyConfigDefaultsForMode_emptyDefaultsTelemetryFalse(t *testing.T) { } } +func TestApplyConfigDefaultsForMode_emptyDefaultsExperimentalModeFalse(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeEmpty, BaseDirectory: t.TempDir()}) + cfg := &SessionConfig{} + c.applyConfigDefaultsForMode(cfg) + if cfg.EnableExperimentalMode == nil || *cfg.EnableExperimentalMode != false { + t.Errorf("expected experimental mode default false in empty mode, got %v", cfg.EnableExperimentalMode) + } +} + +func TestApplyConfigDefaultsForMode_copilotCliLeavesExperimentalModeNil(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeCopilotCli}) + cfg := &SessionConfig{} + c.applyConfigDefaultsForMode(cfg) + if cfg.EnableExperimentalMode != nil { + t.Errorf("non-empty mode must not default experimental mode") + } +} + func TestApplyConfigDefaultsForMode_emptyHonorsCallerTelemetry(t *testing.T) { c := NewClient(&ClientOptions{Mode: ModeEmpty, BaseDirectory: t.TempDir()}) trueVal := true @@ -398,3 +416,21 @@ func TestApplyConfigDefaultsForMode_copilotCliLeavesMCPOAuthTokenStorageEmpty(t t.Errorf("non-empty mode must not default MCPOAuthTokenStorage, got %q", cfg.MCPOAuthTokenStorage) } } + +func TestApplyResumeDefaultsForMode_emptyDefaultsExperimentalModeFalse(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeEmpty, BaseDirectory: t.TempDir()}) + cfg := &ResumeSessionConfig{} + c.applyResumeDefaultsForMode(cfg) + if cfg.EnableExperimentalMode == nil || *cfg.EnableExperimentalMode != false { + t.Errorf("expected experimental mode default false in empty mode, got %v", cfg.EnableExperimentalMode) + } +} + +func TestApplyResumeDefaultsForMode_copilotCliLeavesExperimentalModeNil(t *testing.T) { + c := NewClient(&ClientOptions{Mode: ModeCopilotCli}) + cfg := &ResumeSessionConfig{} + c.applyResumeDefaultsForMode(cfg) + if cfg.EnableExperimentalMode != nil { + t.Errorf("non-empty mode must not default experimental mode") + } +} diff --git a/go/types.go b/go/types.go index d625310b1..9b3b53455 100644 --- a/go/types.go +++ b/go/types.go @@ -1287,6 +1287,10 @@ type SessionConfig struct { // Experimental: SessionLimits is part of an experimental runtime accounting // surface and may change or be removed in future SDK or CLI releases. SessionLimits *rpc.SessionLimitsConfig + // EnableExperimentalMode controls whether the session enables experimental + // features. When nil, it defaults to false in [ModeEmpty]; otherwise the + // runtime decides. + EnableExperimentalMode *bool // SkipCustomInstructions, when non-nil, controls whether the runtime loads // custom instruction files. See also [ClientOptions.Mode] = [ModeEmpty]. SkipCustomInstructions *bool @@ -1693,6 +1697,10 @@ type ResumeSessionConfig struct { // Experimental: SessionLimits is part of an experimental runtime accounting // surface and may change or be removed in future SDK or CLI releases. SessionLimits *rpc.SessionLimitsConfig + // EnableExperimentalMode controls whether the session enables experimental + // features. When nil, it defaults to false in [ModeEmpty]; otherwise the + // runtime decides. + EnableExperimentalMode *bool // SkipCustomInstructions, when non-nil, controls whether the runtime loads // custom instruction files. See also [ClientOptions.Mode] = [ModeEmpty]. SkipCustomInstructions *bool @@ -2299,6 +2307,7 @@ type createSessionRequest struct { EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` EnableCitations *bool `json:"enableCitations,omitempty"` SessionLimits *rpc.SessionLimitsConfig `json:"sessionLimits,omitempty"` + IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` @@ -2390,6 +2399,7 @@ type resumeSessionRequest struct { EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` EnableCitations *bool `json:"enableCitations,omitempty"` SessionLimits *rpc.SessionLimitsConfig `json:"sessionLimits,omitempty"` + IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` CoauthorEnabled *bool `json:"coauthorEnabled,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 1959a9ef8..593e881c1 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -130,6 +131,8 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); config.getEnableCitations().ifPresent(request::setEnableCitations); request.setSessionLimits(config.getSessionLimits()); + experimentalModeForMode(mode, config.getEnableExperimentalMode().orElse(null)) + .ifPresent(request::setIsExperimentalMode); if (config.getOnUserInputRequest() != null) { request.setRequestUserInput(true); } @@ -260,6 +263,8 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); config.getEnableCitations().ifPresent(request::setEnableCitations); request.setSessionLimits(config.getSessionLimits()); + experimentalModeForMode(mode, config.getEnableExperimentalMode().orElse(null)) + .ifPresent(request::setIsExperimentalMode); if (config.getOnUserInputRequest() != null) { request.setRequestUserInput(true); } @@ -339,6 +344,13 @@ private static Boolean resolveCustomAgentsLocalOnly(Boolean customAgentsLocalOnl return mode == CopilotClientMode.EMPTY ? true : null; } + private static Optional experimentalModeForMode(CopilotClientMode mode, Boolean supplied) { + if (mode == CopilotClientMode.EMPTY) { + return Optional.of(supplied != null ? supplied : false); + } + return Optional.ofNullable(supplied); + } + /** * Configures a session with handlers from the given config. * diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 1755fddbf..682d6fb7c 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -199,6 +199,10 @@ public final class CreateSessionRequest { @JsonProperty("githubMcpToolConfig") private GitHubMcpToolConfig githubMcpToolConfig; + @JsonProperty("isExperimentalMode") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean isExperimentalMode; + @JsonProperty("requestExitPlanMode") private Boolean requestExitPlanMode; @@ -931,6 +935,30 @@ public void setGitHubMcpToolConfig(GitHubMcpToolConfig config) { this.githubMcpToolConfig = config; } + /** + * Gets the isExperimentalMode flag. + * + * @return the flag + */ + public Boolean getIsExperimentalMode() { + return isExperimentalMode; + } + + /** + * Sets the isExperimentalMode flag. + * + * @param isExperimentalMode + * the flag + */ + public void setIsExperimentalMode(boolean isExperimentalMode) { + this.isExperimentalMode = isExperimentalMode; + } + + /** Clears the isExperimentalMode setting, reverting to the default behavior. */ + public void clearIsExperimentalMode() { + this.isExperimentalMode = null; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 42419e5a3..373f5e812 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -54,6 +54,7 @@ public class ResumeSessionConfig { private Boolean enableSessionTelemetry; private Boolean enableCitations; private SessionLimitsConfig sessionLimits; + private Boolean enableExperimentalMode; private Boolean skipCustomInstructions; private Boolean customAgentsLocalOnly; private Boolean coauthorEnabled; @@ -472,6 +473,52 @@ public ResumeSessionConfig setSessionLimits(SessionLimitsConfig sessionLimits) { return this; } + /** + * Clears the sessionLimits setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + @CopilotExperimental + public ResumeSessionConfig clearSessionLimits() { + this.sessionLimits = null; + return this; + } + + /** + * Controls whether the session enables experimental features. + * + * @return {@code true} when experimental features are enabled, {@code false} + * when they are disabled, or empty to use the mode-specific default + */ + @JsonIgnore + public Optional getEnableExperimentalMode() { + return Optional.ofNullable(enableExperimentalMode); + } + + /** + * Controls whether the session enables experimental features. + * + * @param enableExperimentalMode + * {@code true} to enable experimental features; {@code false} to + * disable them + * @return this config for method chaining + */ + public ResumeSessionConfig setEnableExperimentalMode(boolean enableExperimentalMode) { + this.enableExperimentalMode = enableExperimentalMode; + return this; + } + + /** + * Clears the enableExperimentalMode setting. In {@link CopilotClientMode#EMPTY + * EMPTY} mode this defaults to {@code false}; otherwise the runtime decides. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableExperimentalMode() { + this.enableExperimentalMode = null; + return this; + } + /** * Gets whether custom instruction file loading is suppressed. * @@ -1846,6 +1893,7 @@ public ResumeSessionConfig clone() { copy.enableSessionTelemetry = this.enableSessionTelemetry; copy.enableCitations = this.enableCitations; copy.sessionLimits = this.sessionLimits; + copy.enableExperimentalMode = this.enableExperimentalMode; copy.reasoningEffort = this.reasoningEffort; copy.reasoningSummary = this.reasoningSummary; copy.contextTier = this.contextTier; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 55bd08b91..c3559d7b2 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -204,6 +204,10 @@ public final class ResumeSessionRequest { @JsonProperty("githubMcpToolConfig") private GitHubMcpToolConfig githubMcpToolConfig; + @JsonProperty("isExperimentalMode") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean isExperimentalMode; + @JsonProperty("requestExitPlanMode") private Boolean requestExitPlanMode; @@ -956,6 +960,30 @@ public void setGitHubMcpToolConfig(GitHubMcpToolConfig config) { this.githubMcpToolConfig = config; } + /** + * Gets the isExperimentalMode flag. + * + * @return the flag + */ + public Boolean getIsExperimentalMode() { + return isExperimentalMode; + } + + /** + * Sets the isExperimentalMode flag. + * + * @param isExperimentalMode + * the flag + */ + public void setIsExperimentalMode(boolean isExperimentalMode) { + this.isExperimentalMode = isExperimentalMode; + } + + /** Clears the isExperimentalMode setting, reverting to the default behavior. */ + public void clearIsExperimentalMode() { + this.isExperimentalMode = null; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 7033ba557..827a00df6 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -58,6 +58,7 @@ public class SessionConfig { private Boolean enableSessionTelemetry; private Boolean enableCitations; private SessionLimitsConfig sessionLimits; + private Boolean enableExperimentalMode; private Boolean skipCustomInstructions; private Boolean customAgentsLocalOnly; private Boolean coauthorEnabled; @@ -574,6 +575,52 @@ public SessionConfig setSessionLimits(SessionLimitsConfig sessionLimits) { return this; } + /** + * Clears the sessionLimits setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + @CopilotExperimental + public SessionConfig clearSessionLimits() { + this.sessionLimits = null; + return this; + } + + /** + * Controls whether the session enables experimental features. + * + * @return {@code true} when experimental features are enabled, {@code false} + * when they are disabled, or empty to use the mode-specific default + */ + @JsonIgnore + public Optional getEnableExperimentalMode() { + return Optional.ofNullable(enableExperimentalMode); + } + + /** + * Controls whether the session enables experimental features. + * + * @param enableExperimentalMode + * {@code true} to enable experimental features; {@code false} to + * disable them + * @return this config instance for method chaining + */ + public SessionConfig setEnableExperimentalMode(boolean enableExperimentalMode) { + this.enableExperimentalMode = enableExperimentalMode; + return this; + } + + /** + * Clears the enableExperimentalMode setting. In {@link CopilotClientMode#EMPTY + * EMPTY} mode this defaults to {@code false}; otherwise the runtime decides. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableExperimentalMode() { + this.enableExperimentalMode = null; + return this; + } + /** * Gets whether custom instruction file loading is suppressed. * @@ -1981,6 +2028,7 @@ public SessionConfig clone() { copy.enableSessionTelemetry = this.enableSessionTelemetry; copy.enableCitations = this.enableCitations; copy.sessionLimits = this.sessionLimits; + copy.enableExperimentalMode = this.enableExperimentalMode; copy.skipCustomInstructions = this.skipCustomInstructions; copy.customAgentsLocalOnly = this.customAgentsLocalOnly; copy.coauthorEnabled = this.coauthorEnabled; diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 505f93a5a..397ed607a 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -134,6 +134,26 @@ void testBuildCreateRequestSetsReasoningSummary() { assertEquals("concise", request.getReasoningSummary()); } + @Test + void testBuildCreateRequestSetsEnableExperimentalMode() { + var config = new SessionConfig().setEnableExperimentalMode(false); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + assertFalse(request.getIsExperimentalMode()); + } + + @Test + void testBuildCreateRequestOmitsEnableExperimentalModeWhenNotSet() { + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(new SessionConfig()); + assertNull(request.getIsExperimentalMode()); + } + + @Test + void testBuildCreateRequestDefaultsEnableExperimentalModeFalseInEmptyMode() { + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "sid-empty", + CopilotClientMode.EMPTY); + assertFalse(request.getIsExperimentalMode()); + } + @Test void testBuildCreateRequestSetsContextTier() { var config = new SessionConfig().setContextTier("long_context"); @@ -240,6 +260,27 @@ void testBuildResumeRequestOmitsEnableSessionTelemetryWhenNotSet() { assertNull(request.getEnableSessionTelemetry()); } + @Test + void testBuildResumeRequestSetsEnableExperimentalMode() { + var config = new ResumeSessionConfig().setEnableExperimentalMode(true); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + assertTrue(request.getIsExperimentalMode()); + } + + @Test + void testBuildResumeRequestOmitsEnableExperimentalModeWhenNotSet() { + var config = new ResumeSessionConfig(); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + assertNull(request.getIsExperimentalMode()); + } + + @Test + void testBuildResumeRequestDefaultsEnableExperimentalModeFalseInEmptyMode() { + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-empty", new ResumeSessionConfig(), + CopilotClientMode.EMPTY); + assertFalse(request.getIsExperimentalMode()); + } + @Test void testBuildResumeRequestWithTools() { var tool = ToolDefinition.create("my_tool", "A tool", Map.of("type", "object"), diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 78290bf68..c0cb6ed17 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1325,6 +1325,11 @@ export class CopilotClient { return {}; } + /** Mode-specific default for enableExperimentalMode. */ + private experimentalModeForMode(supplied: boolean | undefined): boolean | undefined { + return this.options.mode === "empty" ? (supplied ?? false) : supplied; + } + /** * Returns the systemMessage config to use, adjusted for the current mode. * In empty mode we ensure the environment_context section is removed @@ -1520,6 +1525,7 @@ export class CopilotClient { clientName: config.clientName, reasoningEffort: config.reasoningEffort, reasoningSummary: config.reasoningSummary, + isExperimentalMode: this.experimentalModeForMode(config.enableExperimentalMode), contextTier: config.contextTier, tools: config.tools?.map((tool) => ({ name: tool.name, @@ -1759,6 +1765,7 @@ export class CopilotClient { model: config.model, reasoningEffort: config.reasoningEffort, reasoningSummary: config.reasoningSummary, + isExperimentalMode: this.experimentalModeForMode(config.enableExperimentalMode), contextTier: config.contextTier, systemMessage: wireSystemMessage, availableTools: toolFilterOptions.availableTools, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index acba82667..b677808ec 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2074,6 +2074,12 @@ export interface SessionConfigBase { */ reasoningSummary?: ReasoningSummary; + /** + * Controls whether the session enables experimental features. + * Defaults to `false` in `"empty"` mode; otherwise the runtime decides when unset. + */ + enableExperimentalMode?: boolean; + /** * Context window tier for models that support it. Use "long_context" to pin * the session to the long-context tier; omit or use "default" otherwise. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 124261527..78fbe0426 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { EventEmitter } from "node:events"; import { PassThrough } from "stream"; +import { mkdtempSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; import { describe, expect, it, onTestFinished, vi } from "vitest"; import { approveAll, @@ -484,6 +487,100 @@ describe("CopilotClient", () => { expect(resumePayload.reasoningSummary).toBe("none"); }); + it("forwards enableExperimentalMode in session.create and session.resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + const session = await client.createSession({ + onPermissionRequest: approveAll, + enableExperimentalMode: false, + }); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + enableExperimentalMode: true, + }); + + const createPayload = spy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + const resumePayload = spy.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as any; + expect(createPayload.isExperimentalMode).toBe(false); + expect(resumePayload.isExperimentalMode).toBe(true); + }); + + it("defaults enableExperimentalMode by client mode", async () => { + const baseDirectory = mkdtempSync(join(tmpdir(), "copilot-sdk-node-empty-")); + const emptyClient = new CopilotClient({ mode: "empty", baseDirectory }); + await emptyClient.start(); + onTestFinished(() => emptyClient.forceStop()); + + const emptySpy = vi + .spyOn((emptyClient as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + if (method === "session.options.update") return {}; + throw new Error(`Unexpected method: ${method}`); + }); + + const emptySession = await emptyClient.createSession({ + onPermissionRequest: approveAll, + availableTools: [], + }); + await emptyClient.resumeSession(emptySession.sessionId, { + onPermissionRequest: approveAll, + availableTools: [], + }); + + const emptyCreatePayload = emptySpy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + const emptyResumePayload = emptySpy.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as any; + expect(emptyCreatePayload.isExperimentalMode).toBe(false); + expect(emptyResumePayload.isExperimentalMode).toBe(false); + + const cliClient = new CopilotClient(); + await cliClient.start(); + onTestFinished(() => cliClient.forceStop()); + + const cliSpy = vi + .spyOn((cliClient as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + const cliSession = await cliClient.createSession({ + onPermissionRequest: approveAll, + }); + await cliClient.resumeSession(cliSession.sessionId, { + onPermissionRequest: approveAll, + }); + + const cliCreatePayload = cliSpy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + const cliResumePayload = cliSpy.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as any; + expect(cliCreatePayload.isExperimentalMode).toBeUndefined(); + expect(cliResumePayload.isExperimentalMode).toBeUndefined(); + }); + it("forwards contextTier in session.create and session.resume", async () => { const client = new CopilotClient(); await client.start(); diff --git a/python/copilot/_mode.py b/python/copilot/_mode.py index 77212891b..1a9ed6e1f 100644 --- a/python/copilot/_mode.py +++ b/python/copilot/_mode.py @@ -259,6 +259,14 @@ def _custom_agents_local_only_default( return _empty_mode_bool_default(mode, supplied, True) +def _enable_experimental_mode_default( + mode: CopilotClientMode | None, + supplied: bool | None, +) -> bool | None: + """Empty mode defaults experimental mode to False; caller value wins.""" + return _empty_mode_bool_default(mode, supplied, False) + + def _mcp_oauth_token_storage_default( mode: CopilotClientMode | None, supplied: Literal["persistent", "in-memory"] | None, diff --git a/python/copilot/client.py b/python/copilot/client.py index f7f0a4eb2..8f7c2daf0 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -39,6 +39,7 @@ ToolSet, _custom_agents_local_only_default, _embedding_cache_storage_default, + _enable_experimental_mode_default, _enable_file_hooks_default, _enable_host_git_operations_default, _enable_on_demand_instruction_discovery_default, @@ -2019,6 +2020,7 @@ async def create_session( client_name: str | None = None, reasoning_effort: ReasoningEffort | None = None, reasoning_summary: ReasoningSummary | None = None, + enable_experimental_mode: bool | None = None, context_tier: ContextTier | None = None, tools: list[Tool] | None = None, system_message: SystemMessageConfig | None = None, @@ -2105,6 +2107,9 @@ async def create_session( reasoning_summary: Reasoning summary mode for supported models. Use ``"none"`` to suppress summary output regardless of whether reasoning is enabled. + enable_experimental_mode: Controls whether the session enables + experimental features. Defaults to ``False`` in ``"empty"`` + mode; otherwise the runtime decides when omitted. context_tier: Context window tier for models that support it. Use ``"long_context"`` to pin the session to the long-context tier. tools: Custom tools to register with the session. @@ -2297,6 +2302,7 @@ async def create_session( enable_session_store = _enable_session_store_default(mode, enable_session_store) enable_skills = _enable_skills_default(mode, enable_skills) custom_agents_local_only = _custom_agents_local_only_default(mode, custom_agents_local_only) + enable_experimental_mode = _enable_experimental_mode_default(mode, enable_experimental_mode) payload: dict[str, Any] = {} if model: @@ -2307,6 +2313,8 @@ async def create_session( payload["reasoningEffort"] = reasoning_effort if reasoning_summary: payload["reasoningSummary"] = reasoning_summary + if enable_experimental_mode is not None: + payload["isExperimentalMode"] = enable_experimental_mode if context_tier: payload["contextTier"] = context_tier if tool_defs: @@ -2708,6 +2716,7 @@ async def resume_session( client_name: str | None = None, reasoning_effort: ReasoningEffort | None = None, reasoning_summary: ReasoningSummary | None = None, + enable_experimental_mode: bool | None = None, context_tier: ContextTier | None = None, tools: list[Tool] | None = None, system_message: SystemMessageConfig | None = None, @@ -2795,6 +2804,9 @@ async def resume_session( reasoning_summary: Reasoning summary mode for supported models. Use ``"none"`` to suppress summary output regardless of whether reasoning is enabled. + enable_experimental_mode: Controls whether the session enables + experimental features. Defaults to ``False`` in ``"empty"`` + mode; otherwise the runtime decides when omitted. context_tier: Context window tier for models that support it. Use ``"long_context"`` to pin the session to the long-context tier. tools: Custom tools to register with the session. @@ -2987,6 +2999,7 @@ async def resume_session( enable_session_store = _enable_session_store_default(mode, enable_session_store) enable_skills = _enable_skills_default(mode, enable_skills) custom_agents_local_only = _custom_agents_local_only_default(mode, custom_agents_local_only) + enable_experimental_mode = _enable_experimental_mode_default(mode, enable_experimental_mode) payload: dict[str, Any] = {"sessionId": session_id} @@ -2998,6 +3011,8 @@ async def resume_session( payload["reasoningEffort"] = reasoning_effort if reasoning_summary: payload["reasoningSummary"] = reasoning_summary + if enable_experimental_mode is not None: + payload["isExperimentalMode"] = enable_experimental_mode if context_tier: payload["contextTier"] = context_tier if tool_defs: diff --git a/python/test_client.py b/python/test_client.py index ba353bde3..0f3ba8136 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -7,6 +7,7 @@ import asyncio import inspect from datetime import UTC, datetime +from tempfile import TemporaryDirectory from unittest.mock import AsyncMock, Mock, patch import pytest @@ -577,6 +578,108 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_enable_experimental_mode(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_experimental_mode=False, + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + enable_experimental_mode=True, + ) + + assert captured["session.create"]["isExperimentalMode"] is False + assert captured["session.resume"]["isExperimentalMode"] is True + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_create_and_resume_session_default_enable_experimental_mode_by_mode(self): + with TemporaryDirectory() as base_directory: + client = CopilotClient( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + mode="empty", + base_directory=base_directory, + ) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + if method == "session.options.update": + return {"success": True} + return {} + + client._client.request = mock_request + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + available_tools=[], + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + available_tools=[], + ) + + assert captured["session.create"]["isExperimentalMode"] is False + assert captured["session.resume"]["isExperimentalMode"] is False + finally: + await client.force_stop() + + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + ) + + assert "isExperimentalMode" not in captured["session.create"] + assert "isExperimentalMode" not in captured["session.resume"] + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_forward_context_tier(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) diff --git a/rust/src/mode.rs b/rust/src/mode.rs index a80420c19..2b1ab897c 100644 --- a/rust/src/mode.rs +++ b/rust/src/mode.rs @@ -291,6 +291,15 @@ pub(crate) fn memory_for_mode( } } +/// Returns the `enable_experimental_mode` value to send for the given mode. +pub(crate) fn experimental_mode_for_mode(mode: ClientMode, supplied: Option) -> Option { + if mode == ClientMode::Empty { + Some(supplied.unwrap_or(false)) + } else { + supplied + } +} + #[cfg(test)] mod tests { use super::*; @@ -534,4 +543,28 @@ mod tests { Some(MemoryConfiguration::enabled()) ); } + + #[test] + fn experimental_mode_defaults_false_in_empty_mode() { + assert_eq!( + experimental_mode_for_mode(ClientMode::Empty, None), + Some(false) + ); + assert_eq!( + experimental_mode_for_mode(ClientMode::Empty, Some(true)), + Some(true) + ); + assert_eq!( + experimental_mode_for_mode(ClientMode::Empty, Some(false)), + Some(false) + ); + } + + #[test] + fn experimental_mode_remains_runtime_controlled_in_copilot_cli_mode() { + assert_eq!( + experimental_mode_for_mode(ClientMode::CopilotCli, None), + None + ); + } } diff --git a/rust/src/session.rs b/rust/src/session.rs index 6919f2813..d505541a5 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -852,6 +852,8 @@ impl Client { config.system_message = crate::mode::system_message_for_mode(mode, config.system_message.take()); config.memory = crate::mode::memory_for_mode(mode, config.memory.take()); + config.enable_experimental_mode = + crate::mode::experimental_mode_for_mode(mode, config.enable_experimental_mode); if mode == crate::ClientMode::Empty { if config.enable_session_telemetry.is_none() { config.enable_session_telemetry = Some(false); @@ -1120,6 +1122,8 @@ impl Client { config.system_message = crate::mode::system_message_for_mode(mode, config.system_message.take()); config.memory = crate::mode::memory_for_mode(mode, config.memory.take()); + config.enable_experimental_mode = + crate::mode::experimental_mode_for_mode(mode, config.enable_experimental_mode); if mode == crate::ClientMode::Empty { if config.enable_session_telemetry.is_none() { config.enable_session_telemetry = Some(false); diff --git a/rust/src/types.rs b/rust/src/types.rs index b5c180e4a..1c5e457a3 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -2074,6 +2074,11 @@ pub struct SessionConfig { /// the initial create request and maintained via `session.options.update`. /// Defaults to `true` in [`crate::ClientMode::Empty`] when unset. pub custom_agents_local_only: Option, + /// Controls whether the session enables experimental features. + /// + /// Defaults to `false` in [`crate::ClientMode::Empty`] when unset; + /// in `copilot-cli` mode, leaving this unset lets the runtime decide. + pub enable_experimental_mode: Option, /// Whether to include the `Co-authored-by` trailer in commit messages. /// Applied via `session.options.update` after create/resume. Defaults to /// `false` in [`crate::ClientMode::Empty`] when unset. @@ -2166,6 +2171,7 @@ impl std::fmt::Debug for SessionConfig { .field("commands", &self.commands) .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) + .field("enable_experimental_mode", &self.enable_experimental_mode) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -2287,6 +2293,7 @@ impl Default for SessionConfig { system_message_transform: None, skip_custom_instructions: None, custom_agents_local_only: None, + enable_experimental_mode: None, coauthor_enabled: None, manage_schedule_enabled: None, } @@ -2436,6 +2443,7 @@ impl SessionConfig { commands: wire_commands, exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, + is_experimental_mode: self.enable_experimental_mode, }; let runtime = SessionConfigRuntime { @@ -2999,6 +3007,12 @@ impl SessionConfig { self } + /// Set [`enable_experimental_mode`](Self::enable_experimental_mode). + pub fn with_enable_experimental_mode(mut self, enable_experimental_mode: bool) -> Self { + self.enable_experimental_mode = Some(enable_experimental_mode); + self + } + /// Set [`Self::coauthor_enabled`]. pub fn with_coauthor_enabled(mut self, value: bool) -> Self { self.coauthor_enabled = Some(value); @@ -3262,6 +3276,11 @@ pub struct ResumeSessionConfig { pub skip_custom_instructions: Option, /// See [`SessionConfig::custom_agents_local_only`]. pub custom_agents_local_only: Option, + /// Controls whether the session enables experimental features. + /// + /// Defaults to `false` in [`crate::ClientMode::Empty`] when unset; + /// in `copilot-cli` mode, leaving this unset lets the runtime decide. + pub enable_experimental_mode: Option, /// See [`SessionConfig::coauthor_enabled`]. pub coauthor_enabled: Option, /// See [`SessionConfig::manage_schedule_enabled`]. @@ -3350,6 +3369,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("commands", &self.commands) .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) + .field("enable_experimental_mode", &self.enable_experimental_mode) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -3504,6 +3524,7 @@ impl ResumeSessionConfig { commands: wire_commands, exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, + is_experimental_mode: self.enable_experimental_mode, suppress_resume_event: self.suppress_resume_event, continue_pending_work: self.continue_pending_work, }; @@ -3609,6 +3630,7 @@ impl ResumeSessionConfig { system_message_transform: None, skip_custom_instructions: None, custom_agents_local_only: None, + enable_experimental_mode: None, coauthor_enabled: None, manage_schedule_enabled: None, } @@ -4145,6 +4167,12 @@ impl ResumeSessionConfig { self } + /// Set [`enable_experimental_mode`](Self::enable_experimental_mode). + pub fn with_enable_experimental_mode(mut self, enable_experimental_mode: bool) -> Self { + self.enable_experimental_mode = Some(enable_experimental_mode); + self + } + /// Set [`Self::coauthor_enabled`]. pub fn with_coauthor_enabled(mut self, value: bool) -> Self { self.coauthor_enabled = Some(value); @@ -7364,4 +7392,61 @@ mod permission_builder_tests { PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) )); } + + #[test] + fn session_config_enable_experimental_mode_serializes_when_set() { + let cfg = SessionConfig::default().with_enable_experimental_mode(false); + assert_eq!(cfg.enable_experimental_mode, Some(false)); + + let (wire, _runtime) = cfg + .into_wire(Some(SessionId::from("experimental-mode"))) + .expect("enable_experimental_mode config has no duplicate handlers"); + assert_eq!(wire.is_experimental_mode, Some(false)); + + let json = serde_json::to_value(&wire).unwrap(); + assert_eq!(json["isExperimentalMode"], serde_json::Value::Bool(false)); + } + + #[test] + fn session_config_enable_experimental_mode_omitted_when_none() { + let cfg = SessionConfig::default(); + assert_eq!(cfg.enable_experimental_mode, None); + + let (wire, _runtime) = cfg + .into_wire(Some(SessionId::from("no-experimental-mode"))) + .expect("default config has no duplicate handlers"); + assert_eq!(wire.is_experimental_mode, None); + + let json = serde_json::to_value(&wire).unwrap(); + assert!(json.get("isExperimentalMode").is_none()); + } + + #[test] + fn resume_session_config_enable_experimental_mode_serializes_when_set() { + let cfg = ResumeSessionConfig::new(SessionId::from("resume-experimental-mode")) + .with_enable_experimental_mode(false); + assert_eq!(cfg.enable_experimental_mode, Some(false)); + + let (wire, _runtime) = cfg + .into_wire() + .expect("resume enable_experimental_mode config has no duplicate handlers"); + assert_eq!(wire.is_experimental_mode, Some(false)); + + let json = serde_json::to_value(&wire).unwrap(); + assert_eq!(json["isExperimentalMode"], serde_json::Value::Bool(false)); + } + + #[test] + fn resume_session_config_enable_experimental_mode_omitted_when_none() { + let cfg = ResumeSessionConfig::new(SessionId::from("resume-no-experimental-mode")); + assert_eq!(cfg.enable_experimental_mode, None); + + let (wire, _runtime) = cfg + .into_wire() + .expect("default resume config has no duplicate handlers"); + assert_eq!(wire.is_experimental_mode, None); + + let json = serde_json::to_value(&wire).unwrap(); + assert!(json.get("isExperimentalMode").is_none()); + } } diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 4dc756909..1620e5ee1 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -180,6 +180,8 @@ pub(crate) struct SessionCreateWire { pub exp_assignments: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_managed_settings: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_experimental_mode: Option, } /// The exact JSON shape sent on the `session.resume` JSON-RPC request. @@ -323,4 +325,6 @@ pub(crate) struct SessionResumeWire { pub exp_assignments: Option, #[serde(skip_serializing_if = "Option::is_none")] pub enable_managed_settings: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_experimental_mode: Option, } From ede6b3ebcee115fabb9e5bb755e9c31ee4550e31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:26:10 +0000 Subject: [PATCH 09/72] build(deps): bump brace-expansion (#2191) Bumps the npm_and_yarn group with 1 update in the /scripts/docs-validation directory: [brace-expansion](https://github.com/juliangruber/brace-expansion). Updates `brace-expansion` from 5.0.6 to 5.0.9 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.6...v5.0.9) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 5.0.9 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- scripts/docs-validation/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/docs-validation/package-lock.json b/scripts/docs-validation/package-lock.json index 7b7ca5a65..0c2751fa7 100644 --- a/scripts/docs-validation/package-lock.json +++ b/scripts/docs-validation/package-lock.json @@ -480,15 +480,15 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/color-convert": { From 1b9ac0722fc2025807717d5f98f7b014b0298da6 Mon Sep 17 00:00:00 2001 From: examon <1676292+examon@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:26:33 +0200 Subject: [PATCH 10/72] docs: remove the nonexistent toolName field from tool.execution_complete (#2212) The Node.js extension-authoring docs listed a `toolName` field on the `tool.execution_complete` session event, in two event-field reference tables and three handler code-comments. That event has no `toolName`: `ToolExecutionCompleteData` declares `required: ["toolCallId","success"]` with `additionalProperties: false` and no `toolName` property, so reading `event.data.toolName` in a completion handler is always undefined. `toolName` is emitted on `tool.execution_start`, and a completion is correlated to its start by `toolCallId`. Remove the field from the two `tool.execution_complete` rows and the three completion-handler comments. It stays on the `tool.execution_start` rows. The repository's shared event reference in docs/features/streaming-events.md already documents the event without it. Co-authored-by: examon --- nodejs/docs/agent-author.md | 4 ++-- nodejs/docs/examples.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nodejs/docs/agent-author.md b/nodejs/docs/agent-author.md index 907181442..fa4bfb1ba 100644 --- a/nodejs/docs/agent-author.md +++ b/nodejs/docs/agent-author.md @@ -258,7 +258,7 @@ Subscribe to session events. Returns an unsubscribe function. ```js const unsub = session.on("tool.execution_complete", (event) => { - // event.data.toolName, event.data.success, event.data.result + // event.data.success, event.data.result }); ``` @@ -268,7 +268,7 @@ const unsub = session.on("tool.execution_complete", (event) => { | ------------------------- | ------------------------------------------------------ | | `assistant.message` | `content`, `messageId` | | `tool.execution_start` | `toolCallId`, `toolName`, `arguments` | -| `tool.execution_complete` | `toolCallId`, `toolName`, `success`, `result`, `error` | +| `tool.execution_complete` | `toolCallId`, `success`, `result`, `error` | | `user.message` | `content`, `attachments`, `source` | | `session.idle` | `backgroundTasks` | | `session.error` | `errorType`, `message`, `stack` | diff --git a/nodejs/docs/examples.md b/nodejs/docs/examples.md index 1bac87982..a1c016cdf 100644 --- a/nodejs/docs/examples.md +++ b/nodejs/docs/examples.md @@ -368,7 +368,7 @@ session.on((event) => { ```js const unsubscribe = session.on("tool.execution_complete", (event) => { - // event.data.toolName, event.data.success, event.data.result, event.data.error + // event.data.success, event.data.result, event.data.error }); // Later, stop listening @@ -417,7 +417,7 @@ session.on("assistant.message", (event) => { | `assistant.message` | Agent's final response | `content`, `messageId`, `toolRequests` | | `assistant.message_delta` | Message content chunks (ephemeral) | `deltaContent` | | `tool.execution_start` | A tool is about to run | `toolCallId`, `toolName`, `arguments` | -| `tool.execution_complete` | A tool finished running | `toolCallId`, `toolName`, `success`, `result`, `error` | +| `tool.execution_complete` | A tool finished running | `toolCallId`, `success`, `result`, `error` | | `user.message` | User sent a message | `content`, `attachments`, `source` | | `session.idle` | Session finished processing a turn | `backgroundTasks` | | `session.error` | An error occurred | `errorType`, `message`, `stack` | @@ -677,6 +677,6 @@ session.on("assistant.message", (event) => { }); session.on("tool.execution_complete", (event) => { - // event.data.success, event.data.toolName, event.data.result + // event.data.success, event.data.result }); ``` From dd8d8691ed98240267bf3652c853e8b7188b7ca5 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 4 Aug 2026 03:51:17 +1000 Subject: [PATCH 11/72] feat: add support for additional directories in session configuration (#2180) * feat: add support for additional directories in session configuration - Introduced `additionalDirectories` field in various session-related configurations across SDKs (Java, Go, .NET, Node.js, Python, Rust). - Updated session creation and resume methods to handle `additionalDirectories`. - Enhanced tests to verify that additional directories are correctly forwarded during session creation and resumption. - Updated documentation to reflect the new configuration options. * fix: address SDK CI checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: rerun SDK checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(rust): isolate E2E cache state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/troubleshooting/compatibility.md | 1 + dotnet/src/Client.cs | 12 ++-- dotnet/src/Types.cs | 7 ++ .../test/Unit/ClientSessionLifetimeTests.cs | 32 +++++++++ dotnet/test/Unit/CloneTests.cs | 8 +++ go/client.go | 2 + go/client_test.go | 54 +++++++++++++++ go/types.go | 8 +++ .../github/copilot/SessionRequestBuilder.java | 2 + .../copilot/rpc/CreateSessionRequest.java | 18 +++++ .../copilot/rpc/ResumeSessionConfig.java | 25 +++++++ .../copilot/rpc/ResumeSessionRequest.java | 18 +++++ .../com/github/copilot/rpc/SessionConfig.java | 25 +++++++ .../com/github/copilot/ConfigCloneTest.java | 6 ++ .../copilot/SessionRequestBuilderTest.java | 14 ++++ nodejs/src/client.ts | 2 + nodejs/src/types.ts | 7 ++ nodejs/test/client.test.ts | 36 ++++++++++ python/copilot/client.py | 6 ++ python/test_client.py | 40 +++++++++++ rust/src/types.rs | 66 +++++++++++++++++++ rust/src/wire.rs | 4 ++ rust/tests/e2e/support.rs | 39 +++++++++++ 23 files changed, 428 insertions(+), 4 deletions(-) diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index c68d59cc7..795c0f5fd 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -84,6 +84,7 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b | Config directory | `configDir` config | Override default config location | | Client name | `clientName` config | Identify app in User-Agent | | Working directory | `workingDirectory` config | Set session cwd | +| Additional directories | `additionalDirectories` config | Grant session access beyond the working directory; re-supply on resume | | **Experimental** | | | | Agent management | `session.rpc.agent.*` | List, select, deselect, get current agent | | Fleet mode | `session.rpc.fleet.start()` | Parallel sub-agent execution; see [Fleet mode](../features/fleet-mode.md) | diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 9b50d6942..b1199dac8 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1203,7 +1203,8 @@ public async Task CreateSessionAsync(SessionConfig config, Cance ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, - EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); + EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, + AdditionalDirectories: config.AdditionalDirectories); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -1420,7 +1421,8 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, - EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null); + EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, + AdditionalDirectories: config.AdditionalDirectories); var rpcTimestamp = Stopwatch.GetTimestamp(); var response = await InvokeRpcAsync( @@ -2775,7 +2777,8 @@ internal record CreateSessionRequest( [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, - [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); + [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, + IList? AdditionalDirectories = null); #pragma warning restore GHCP001 internal record ToolDefinition( @@ -2884,7 +2887,8 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, - [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null); + [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, + IList? AdditionalDirectories = null); #pragma warning restore GHCP001 internal record ResumeSessionResponse( diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index e0b3d1914..e1ba0355f 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3099,6 +3099,7 @@ protected SessionConfigBase(SessionConfigBase? other) SystemMessage = other.SystemMessage; Tools = other.Tools is not null ? [.. other.Tools] : null; WorkingDirectory = other.WorkingDirectory; + AdditionalDirectories = other.AdditionalDirectories is not null ? [.. other.AdditionalDirectories] : null; } /// Client name to identify the application using the SDK. @@ -3372,6 +3373,12 @@ protected SessionConfigBase(SessionConfigBase? other) /// Working directory for the session. public string? WorkingDirectory { get; set; } + /// + /// Additional directories the agent may access beyond . + /// Relative paths resolve against the session working directory. Re-supply them when resuming. + /// + public IList? AdditionalDirectories { get; set; } + /// /// Enable streaming of assistant message and reasoning chunks. /// When true, assistant.message_delta and assistant.reasoning_delta events diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index e1143db17..a30e6d303 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -255,6 +255,38 @@ public async Task CreateSessionAsync_Omits_CustomAgent_ReasoningEffort_When_Unse Assert.False(agent.TryGetProperty("reasoningEffort", out _)); } + [Fact] + public async Task SessionRequests_Serialize_AdditionalDirectories() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + + await using var created = await client.CreateSessionAsync(new SessionConfig + { + AdditionalDirectories = ["/repo/shared", "/repo/generated"], + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var createRequest = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.Collection( + createRequest.Params.GetProperty("additionalDirectories").EnumerateArray(), + value => Assert.Equal("/repo/shared", value.GetString()), + value => Assert.Equal("/repo/generated", value.GetString())); + + server.ClearRequests(); + + await using var resumed = await client.ResumeSessionAsync("resume-with-additional-directories", new ResumeSessionConfig + { + AdditionalDirectories = ["/repo/resumed"], + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var resumeRequest = Assert.Single(server.Requests, request => request.Method == "session.resume"); + Assert.Collection( + resumeRequest.Params.GetProperty("additionalDirectories").EnumerateArray(), + value => Assert.Equal("/repo/resumed", value.GetString())); + } + [Fact] public async Task CreateSessionAsync_Registers_McpAuth_Interest_Only_When_Handler_Configured() { diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index e14003694..ad8b0644e 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -75,6 +75,7 @@ public void SessionConfig_Clone_CopiesAllProperties() ExcludedTools = ["tool3"], ExcludedBuiltInAgents = ["explore", "task"], WorkingDirectory = "/workspace", + AdditionalDirectories = ["/shared", "/generated"], Streaming = true, EnableCitations = true, EnableSessionTelemetry = false, @@ -120,6 +121,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Assert.Equal(original.ExcludedTools, clone.ExcludedTools); Assert.Equal(original.ExcludedBuiltInAgents, clone.ExcludedBuiltInAgents); Assert.Equal(original.WorkingDirectory, clone.WorkingDirectory); + Assert.Equal(original.AdditionalDirectories, clone.AdditionalDirectories); Assert.Equal(original.Streaming, clone.Streaming); Assert.Equal(original.EnableCitations, clone.EnableCitations); Assert.Equal(original.EnableSessionTelemetry, clone.EnableSessionTelemetry); @@ -156,6 +158,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() ExcludedBuiltInAgents = ["explore"], McpServers = new Dictionary { ["s1"] = new McpStdioServerConfig { Command = "echo" } }, CustomAgents = [new CustomAgentConfig { Name = "a1" }], + AdditionalDirectories = ["/shared"], SkillDirectories = ["/skills"], InstructionDirectories = ["/instructions"], DisabledSkills = ["skill1"], @@ -169,6 +172,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() clone.ExcludedBuiltInAgents!.Add("task"); clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" }; clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" }); + clone.AdditionalDirectories!.Add("/generated"); clone.SkillDirectories!.Add("/more"); clone.InstructionDirectories!.Add("/more-instructions"); clone.DisabledSkills!.Add("skill99"); @@ -179,6 +183,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() Assert.Single(original.ExcludedBuiltInAgents!); Assert.Single(original.McpServers!); Assert.Single(original.CustomAgents!); + Assert.Single(original.AdditionalDirectories!); Assert.Single(original.SkillDirectories!); Assert.Single(original.InstructionDirectories!); Assert.Single(original.DisabledSkills!); @@ -205,6 +210,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() ExcludedBuiltInAgents = ["explore"], McpServers = new Dictionary { ["s1"] = new McpStdioServerConfig { Command = "echo" } }, CustomAgents = [new CustomAgentConfig { Name = "a1" }], + AdditionalDirectories = ["/shared"], SkillDirectories = ["/skills"], InstructionDirectories = ["/instructions"], DisabledSkills = ["skill1"], @@ -218,6 +224,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() clone.ExcludedBuiltInAgents!.Add("task"); clone.McpServers!["s2"] = new McpStdioServerConfig { Command = "echo" }; clone.CustomAgents!.Add(new CustomAgentConfig { Name = "a2" }); + clone.AdditionalDirectories!.Add("/generated"); clone.SkillDirectories!.Add("/more"); clone.InstructionDirectories!.Add("/more-instructions"); clone.DisabledSkills!.Add("skill99"); @@ -228,6 +235,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() Assert.Single(original.ExcludedBuiltInAgents!); Assert.Single(original.McpServers!); Assert.Single(original.CustomAgents!); + Assert.Single(original.AdditionalDirectories!); Assert.Single(original.SkillDirectories!); Assert.Single(original.InstructionDirectories!); Assert.Single(original.DisabledSkills!); diff --git a/go/client.go b/go/client.go index 34f6d574a..f867b7db2 100644 --- a/go/client.go +++ b/go/client.go @@ -803,6 +803,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.ManageScheduleEnabled = config.ManageScheduleEnabled req.ModelCapabilities = config.ModelCapabilities req.WorkingDirectory = config.WorkingDirectory + req.AdditionalDirectories = config.AdditionalDirectories req.MCPServers = config.MCPServers req.MCPOAuthTokenStorage = config.MCPOAuthTokenStorage req.EnvValueMode = "direct" @@ -1165,6 +1166,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.Hooks = Bool(true) } req.WorkingDirectory = config.WorkingDirectory + req.AdditionalDirectories = config.AdditionalDirectories req.ConfigDir = config.ConfigDirectory req.EnableConfigDiscovery = config.EnableConfigDiscovery req.SkipEmbeddingRetrieval = config.SkipEmbeddingRetrieval diff --git a/go/client_test.go b/go/client_test.go index 2a6db6e86..14131bc4c 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -251,6 +251,60 @@ func TestClient_ForwardsCapiOptionsToSessionRequests(t *testing.T) { assertCapiEnableWebSocketResponses(t, <-resumeParams) } +func TestClient_ForwardsAdditionalDirectoriesToSessionRequests(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + + createParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + createParams <- append(json.RawMessage(nil), params...) + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `","workspacePath":"/workspace"}`), nil + }) + + _, err := client.CreateSession(t.Context(), &SessionConfig{ + AdditionalDirectories: []string{"/repo/shared", "/repo/generated"}, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + assertAdditionalDirectories(t, <-createParams, []string{"/repo/shared", "/repo/generated"}) + + resumeParams := make(chan json.RawMessage, 1) + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + resumeParams <- append(json.RawMessage(nil), params...) + return []byte(`{"sessionId":"resumed-additional-directories","workspacePath":"/workspace"}`), nil + }) + + _, err = client.ResumeSessionWithOptions( + t.Context(), + "resumed-additional-directories", + &ResumeSessionConfig{AdditionalDirectories: []string{"/repo/resumed"}}, + ) + if err != nil { + t.Fatalf("ResumeSessionWithOptions failed: %v", err) + } + assertAdditionalDirectories(t, <-resumeParams, []string{"/repo/resumed"}) +} + +func assertAdditionalDirectories(t *testing.T, params json.RawMessage, want []string) { + t.Helper() + var payload struct { + AdditionalDirectories []string `json:"additionalDirectories"` + } + if err := json.Unmarshal(params, &payload); err != nil { + t.Fatalf("failed to decode request params: %v", err) + } + if !reflect.DeepEqual(payload.AdditionalDirectories, want) { + t.Fatalf("additionalDirectories = %v, want %v", payload.AdditionalDirectories, want) + } +} + func TestClient_ForwardsCanvasProviderToSessionRequests(t *testing.T) { rpcClient, server, _ := newRuntimeShutdownRpcPair(t) t.Cleanup(server.Stop) diff --git a/go/types.go b/go/types.go index 9b3b53455..3ddcf930d 100644 --- a/go/types.go +++ b/go/types.go @@ -1242,6 +1242,9 @@ type SessionConfig struct { // WorkingDirectory is the working directory for the session. // Tool operations will be relative to this directory. WorkingDirectory string + // AdditionalDirectories are directories the agent may access beyond WorkingDirectory. + // Relative paths are resolved against WorkingDirectory. Re-supply them when resuming. + AdditionalDirectories []string // Streaming enables streaming of assistant message and reasoning chunks. // When non-nil and true, assistant.message_delta and assistant.reasoning_delta // events with deltaContent are sent as the response is generated. @@ -1740,6 +1743,9 @@ type ResumeSessionConfig struct { // WorkingDirectory is the working directory for the session. // Tool operations will be relative to this directory. WorkingDirectory string + // AdditionalDirectories are directories the agent may access beyond WorkingDirectory. + // Relative paths are resolved against WorkingDirectory. Re-supply them when resuming. + AdditionalDirectories []string // ConfigDirectory overrides the default configuration directory location. ConfigDirectory string // EnableConfigDiscovery enables runtime discovery of supported configuration. @@ -2319,6 +2325,7 @@ type createSessionRequest struct { RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` Hooks *bool `json:"hooks,omitempty"` WorkingDirectory string `json:"workingDirectory,omitempty"` + AdditionalDirectories []string `json:"additionalDirectories,omitempty"` Streaming *bool `json:"streaming,omitempty"` IncludeSubAgentStreamingEvents *bool `json:"includeSubAgentStreamingEvents,omitempty"` EnableGitHubTelemetryForwarding *bool `json:"enableGitHubTelemetryForwarding,omitempty"` @@ -2411,6 +2418,7 @@ type resumeSessionRequest struct { RequestAutoModeSwitch *bool `json:"requestAutoModeSwitch,omitempty"` Hooks *bool `json:"hooks,omitempty"` WorkingDirectory string `json:"workingDirectory,omitempty"` + AdditionalDirectories []string `json:"additionalDirectories,omitempty"` ConfigDir string `json:"configDir,omitempty"` EnableConfigDiscovery *bool `json:"enableConfigDiscovery,omitempty"` SkipEmbeddingRetrieval *bool `json:"skipEmbeddingRetrieval,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 593e881c1..62c712025 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -140,6 +140,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setHooks(true); } request.setWorkingDirectory(config.getWorkingDirectory()); + request.setAdditionalDirectories(config.getAdditionalDirectories()); if (config.isStreaming()) { request.setStreaming(true); } @@ -272,6 +273,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setHooks(true); } request.setWorkingDirectory(config.getWorkingDirectory()); + request.setAdditionalDirectories(config.getAdditionalDirectories()); request.setConfigDirectory(config.getConfigDirectory()); config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); config.getSkipEmbeddingRetrieval().ifPresent(request::setSkipEmbeddingRetrieval); diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 682d6fb7c..44cb8145c 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -95,6 +95,9 @@ public final class CreateSessionRequest { @JsonProperty("workingDirectory") private String workingDirectory; + @JsonProperty("additionalDirectories") + private List additionalDirectories; + @JsonProperty("streaming") private Boolean streaming; @@ -502,6 +505,21 @@ public void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; } + /** Gets additional directories. @return the additional directories */ + public List getAdditionalDirectories() { + return additionalDirectories; + } + + /** + * Sets additional directories. + * + * @param additionalDirectories + * the additional directories + */ + public void setAdditionalDirectories(List additionalDirectories) { + this.additionalDirectories = additionalDirectories; + } + /** Gets streaming flag. @return the flag */ public Boolean getStreaming() { return streaming; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 373f5e812..35072f129 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -68,6 +68,7 @@ public class ResumeSessionConfig { private UserInputHandler onUserInputRequest; private SessionHooks hooks; private String workingDirectory; + private List additionalDirectories; private String configDirectory; private Boolean enableConfigDiscovery; private Boolean skipEmbeddingRetrieval; @@ -859,6 +860,27 @@ public ResumeSessionConfig setWorkingDirectory(String workingDirectory) { return this; } + /** + * Gets the directories the agent may access beyond the working directory. + * + * @return the additional directory paths + */ + public List getAdditionalDirectories() { + return additionalDirectories; + } + + /** + * Sets directories the agent may access beyond the working directory. + * + * @param additionalDirectories + * the additional directory paths + * @return this config for method chaining + */ + public ResumeSessionConfig setAdditionalDirectories(List additionalDirectories) { + this.additionalDirectories = additionalDirectories; + return this; + } + /** * Gets the configuration directory path. * @@ -1902,6 +1924,9 @@ public ResumeSessionConfig clone() { copy.onUserInputRequest = this.onUserInputRequest; copy.hooks = this.hooks; copy.workingDirectory = this.workingDirectory; + copy.additionalDirectories = this.additionalDirectories != null + ? new ArrayList<>(this.additionalDirectories) + : null; copy.configDirectory = this.configDirectory; copy.enableConfigDiscovery = this.enableConfigDiscovery; copy.skipEmbeddingRetrieval = this.skipEmbeddingRetrieval; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index c3559d7b2..a0ecc2ed5 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -97,6 +97,9 @@ public final class ResumeSessionRequest { @JsonProperty("workingDirectory") private String workingDirectory; + @JsonProperty("additionalDirectories") + private List additionalDirectories; + @JsonProperty("configDir") private String configDirectory; @@ -507,6 +510,21 @@ public void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; } + /** Gets additional directories. @return the additional directories */ + public List getAdditionalDirectories() { + return additionalDirectories; + } + + /** + * Sets additional directories. + * + * @param additionalDirectories + * the additional directories + */ + public void setAdditionalDirectories(List additionalDirectories) { + this.additionalDirectories = additionalDirectories; + } + /** Gets config directory. @return the config directory */ public String getConfigDirectory() { return configDirectory; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 827a00df6..c062b1c7c 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -68,6 +68,7 @@ public class SessionConfig { private UserInputHandler onUserInputRequest; private SessionHooks hooks; private String workingDirectory; + private List additionalDirectories; private boolean streaming; private Boolean includeSubAgentStreamingEvents; private Map mcpServers; @@ -909,6 +910,27 @@ public SessionConfig setWorkingDirectory(String workingDirectory) { return this; } + /** + * Gets the directories the agent may access beyond the working directory. + * + * @return the additional directory paths + */ + public List getAdditionalDirectories() { + return additionalDirectories; + } + + /** + * Sets directories the agent may access beyond the working directory. + * + * @param additionalDirectories + * the additional directory paths + * @return this config instance for method chaining + */ + public SessionConfig setAdditionalDirectories(List additionalDirectories) { + this.additionalDirectories = additionalDirectories; + return this; + } + /** * Returns whether streaming is enabled. * @@ -2037,6 +2059,9 @@ public SessionConfig clone() { copy.onUserInputRequest = this.onUserInputRequest; copy.hooks = this.hooks; copy.workingDirectory = this.workingDirectory; + copy.additionalDirectories = this.additionalDirectories != null + ? new ArrayList<>(this.additionalDirectories) + : null; copy.streaming = this.streaming; copy.includeSubAgentStreamingEvents = this.includeSubAgentStreamingEvents; copy.mcpServers = this.mcpServers != null ? new java.util.HashMap<>(this.mcpServers) : null; diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index 6986ef7f0..a8e7fb2e0 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -440,12 +440,15 @@ void resumeSessionConfigAllSetters() { void sessionConfigNewFieldsCloned() { SessionConfig original = new SessionConfig(); original.setGitHubToken("ghp_per_session_token"); + original.setAdditionalDirectories(new java.util.ArrayList<>(List.of("/repo/shared"))); DefaultAgentConfig defaultAgent = new DefaultAgentConfig().setExcludedTools(List.of("secret_tool")); original.setDefaultAgent(defaultAgent); SessionConfig cloned = original.clone(); assertEquals("ghp_per_session_token", cloned.getGitHubToken()); + assertEquals(List.of("/repo/shared"), cloned.getAdditionalDirectories()); + assertNotSame(original.getAdditionalDirectories(), cloned.getAdditionalDirectories()); assertSame(defaultAgent, cloned.getDefaultAgent()); } @@ -453,12 +456,15 @@ void sessionConfigNewFieldsCloned() { void resumeSessionConfigNewFieldsCloned() { ResumeSessionConfig original = new ResumeSessionConfig(); original.setGitHubToken("ghp_per_session_token"); + original.setAdditionalDirectories(new java.util.ArrayList<>(List.of("/repo/resumed"))); DefaultAgentConfig defaultAgent = new DefaultAgentConfig().setExcludedTools(List.of("secret_tool")); original.setDefaultAgent(defaultAgent); ResumeSessionConfig cloned = original.clone(); assertEquals("ghp_per_session_token", cloned.getGitHubToken()); + assertEquals(List.of("/repo/resumed"), cloned.getAdditionalDirectories()); + assertNotSame(original.getAdditionalDirectories(), cloned.getAdditionalDirectories()); assertSame(defaultAgent, cloned.getDefaultAgent()); } diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 397ed607a..54773662c 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -127,6 +127,13 @@ void testBuildCreateRequestSetsClientName() { assertEquals("my-app", request.getClientName()); } + @Test + void testBuildCreateRequestSetsAdditionalDirectories() { + var config = new SessionConfig().setAdditionalDirectories(List.of("/repo/shared", "/repo/generated")); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + assertEquals(List.of("/repo/shared", "/repo/generated"), request.getAdditionalDirectories()); + } + @Test void testBuildCreateRequestSetsReasoningSummary() { var config = new SessionConfig().setReasoningSummary("concise"); @@ -362,6 +369,13 @@ void testBuildResumeRequestSetsClientName() { assertEquals("my-app", request.getClientName()); } + @Test + void testBuildResumeRequestSetsAdditionalDirectories() { + var config = new ResumeSessionConfig().setAdditionalDirectories(List.of("/repo/resumed")); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-additional-directories", config); + assertEquals(List.of("/repo/resumed"), request.getAdditionalDirectories()); + } + @Test void testBuildCreateRequestPropagatesGranularMultitenancyFields() { var config = new SessionConfig().setSkipEmbeddingRetrieval(true) diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index c0cb6ed17..3e100eddd 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1572,6 +1572,7 @@ export class CopilotClient { requestAutoModeSwitch: !!config.onAutoModeSwitchRequest, hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)), workingDirectory: config.workingDirectory, + additionalDirectories: config.additionalDirectories, streaming: config.streaming, includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true, ...(this.onGitHubTelemetry != null @@ -1814,6 +1815,7 @@ export class CopilotClient { requestAutoModeSwitch: !!config.onAutoModeSwitchRequest, hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)), workingDirectory: config.workingDirectory, + additionalDirectories: config.additionalDirectories, configDir: config.configDirectory, enableConfigDiscovery: config.enableConfigDiscovery, skipEmbeddingRetrieval: config.skipEmbeddingRetrieval, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index b677808ec..a8a9410f8 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2411,6 +2411,13 @@ export interface SessionConfigBase { */ workingDirectory?: string; + /** + * Additional directories the agent may access beyond the working directory. + * Relative paths are resolved against the session's working directory. + * Re-supply these directories when resuming a session. + */ + additionalDirectories?: string[]; + /** * Enable streaming of assistant message and reasoning chunks. * When true, ephemeral assistant.message_delta and assistant.reasoning_delta diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 78fbe0426..bbe6fbe66 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -270,6 +270,42 @@ describe("CopilotClient", () => { ); }); + it("forwards additional directories when creating and resuming sessions", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create" || method === "session.resume") { + return { sessionId: params.sessionId, workspacePath: "/workspace" }; + } + throw new Error(`Unexpected method: ${method}`); + }); + + await client.createSession({ + sessionId: "create-with-additional-directories", + additionalDirectories: ["/repo/shared", "/repo/generated"], + onPermissionRequest: approveAll, + }); + await client.resumeSession("resume-with-additional-directories", { + additionalDirectories: ["/repo/resumed"], + onPermissionRequest: approveAll, + }); + + expect(spy).toHaveBeenCalledWith( + "session.create", + expect.objectContaining({ + additionalDirectories: ["/repo/shared", "/repo/generated"], + }) + ); + expect(spy).toHaveBeenCalledWith( + "session.resume", + expect.objectContaining({ additionalDirectories: ["/repo/resumed"] }) + ); + }); + it("registers MCP OAuth interest after cloud create only when an auth handler is configured", async () => { const client = new CopilotClient(); await client.start(); diff --git a/python/copilot/client.py b/python/copilot/client.py index 8f7c2daf0..737619ef3 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2030,6 +2030,7 @@ async def create_session( on_user_input_request: UserInputHandler | None = None, hooks: SessionHooks | None = None, working_directory: str | None = None, + additional_directories: list[str] | None = None, provider: ProviderConfig | None = None, capi: CapiSessionOptions | None = None, providers: list[NamedProviderConfig] | None = None, @@ -2384,6 +2385,8 @@ async def create_session( # Add working directory if provided if working_directory: payload["workingDirectory"] = working_directory + if additional_directories: + payload["additionalDirectories"] = additional_directories # Add streaming option if provided if streaming is not None: @@ -2726,6 +2729,7 @@ async def resume_session( on_user_input_request: UserInputHandler | None = None, hooks: SessionHooks | None = None, working_directory: str | None = None, + additional_directories: list[str] | None = None, provider: ProviderConfig | None = None, capi: CapiSessionOptions | None = None, providers: list[NamedProviderConfig] | None = None, @@ -3104,6 +3108,8 @@ async def resume_session( if working_directory: payload["workingDirectory"] = working_directory + if additional_directories: + payload["additionalDirectories"] = additional_directories if config_directory: payload["configDir"] = config_directory if enable_config_discovery is not None: diff --git a/python/test_client.py b/python/test_client.py index 0f3ba8136..0bba1ccd7 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -163,6 +163,46 @@ async def test_resume_session_allows_none_permission_handler(self): class TestCreateSessionConfig: + @pytest.mark.asyncio + async def test_additional_directories_forwarded_on_create_and_resume(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured: list[tuple[str, dict]] = [] + + async def mock_request(method, params, **kwargs): + captured.append((method, params)) + if method == "session.create": + result = {"sessionId": params["sessionId"], "workspacePath": None} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + if method == "session.resume": + return {"sessionId": params["sessionId"], "workspacePath": None} + return {} + + client._client.request = mock_request + await client.create_session( + session_id="create-with-additional-directories", + additional_directories=["/repo/shared", "/repo/generated"], + ) + await client.resume_session( + "resume-with-additional-directories", + additional_directories=["/repo/resumed"], + ) + + create_payload = next( + params for method, params in captured if method == "session.create" + ) + resume_payload = next( + params for method, params in captured if method == "session.resume" + ) + assert create_payload["additionalDirectories"] == ["/repo/shared", "/repo/generated"] + assert resume_payload["additionalDirectories"] == ["/repo/resumed"] + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_mcp_auth_handler_registers_interest_in_create_session(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) diff --git a/rust/src/types.rs b/rust/src/types.rs index 1c5e457a3..d2b8dcb93 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1990,6 +1990,10 @@ pub struct SessionConfig { /// Working directory for the session. Tool operations resolve /// relative paths against this directory. pub working_directory: Option, + /// Additional directories the agent may access beyond the working directory. + /// Relative paths resolve against the session working directory. Re-supply + /// them when resuming a session. + pub additional_directories: Option>, /// Per-session GitHub token. Distinct from /// [`ClientOptions::github_token`](crate::ClientOptions::github_token), /// which authenticates the CLI process itself; this token determines @@ -2158,6 +2162,7 @@ impl std::fmt::Debug for SessionConfig { .field("memory", &self.memory) .field("config_directory", &self.config_directory) .field("working_directory", &self.working_directory) + .field("additional_directories", &self.additional_directories) .field( "github_token", &self.github_token.as_ref().map(|_| ""), @@ -2274,6 +2279,7 @@ impl Default for SessionConfig { memory: None, config_directory: None, working_directory: None, + additional_directories: None, github_token: None, remote_session: None, cloud: None, @@ -2435,6 +2441,7 @@ impl SessionConfig { memory: self.memory, config_dir: self.config_directory, working_directory: self.working_directory, + additional_directories: self.additional_directories, github_token: self.github_token, remote_session: self.remote_session, cloud: self.cloud, @@ -2964,6 +2971,16 @@ impl SessionConfig { self } + /// Set directories the agent may access beyond the working directory. + pub fn with_additional_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.additional_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + /// Set the per-session GitHub token. Distinct from /// [`ClientOptions::github_token`](crate::ClientOptions::github_token); /// this token determines the GitHub identity used for content exclusion, @@ -3209,6 +3226,9 @@ pub struct ResumeSessionConfig { pub config_directory: Option, /// Per-session working directory on resume. pub working_directory: Option, + /// Additional directories the agent may access on resume. Relative paths + /// resolve against the session working directory. + pub additional_directories: Option>, /// Per-session GitHub token on resume. See /// [`SessionConfig::github_token`]. pub github_token: Option, @@ -3357,6 +3377,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("memory", &self.memory) .field("config_directory", &self.config_directory) .field("working_directory", &self.working_directory) + .field("additional_directories", &self.additional_directories) .field( "github_token", &self.github_token.as_ref().map(|_| ""), @@ -3517,6 +3538,7 @@ impl ResumeSessionConfig { memory: self.memory, config_dir: self.config_directory, working_directory: self.working_directory, + additional_directories: self.additional_directories, github_token: self.github_token, remote_session: self.remote_session, include_sub_agent_streaming_events: self.include_sub_agent_streaming_events, @@ -3610,6 +3632,7 @@ impl ResumeSessionConfig { memory: None, config_directory: None, working_directory: None, + additional_directories: None, github_token: None, remote_session: None, include_sub_agent_streaming_events: None, @@ -4115,6 +4138,16 @@ impl ResumeSessionConfig { self } + /// Set directories the agent may access beyond the working directory on resume. + pub fn with_additional_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.additional_directories = Some(paths.into_iter().map(Into::into).collect()); + self + } + /// Set the per-session GitHub token on resume. See /// [`SessionConfig::github_token`] for distinction from the /// client-level token. @@ -6427,6 +6460,7 @@ mod tests { .with_agent("researcher") .with_config_directory(PathBuf::from("/tmp/config")) .with_working_directory(PathBuf::from("/tmp/work")) + .with_additional_directories([PathBuf::from("/tmp/shared")]) .with_github_token("ghp_test") .with_capi(CapiSessionOptions::new().with_enable_web_socket_responses(false)) .with_enable_session_telemetry(false) @@ -6464,6 +6498,10 @@ mod tests { assert_eq!(cfg.agent.as_deref(), Some("researcher")); assert_eq!(cfg.config_directory, Some(PathBuf::from("/tmp/config"))); assert_eq!(cfg.working_directory, Some(PathBuf::from("/tmp/work"))); + assert_eq!( + cfg.additional_directories.as_deref(), + Some(&[PathBuf::from("/tmp/shared")][..]) + ); assert_eq!(cfg.github_token.as_deref(), Some("ghp_test")); assert_eq!( cfg.capi, @@ -6498,6 +6536,7 @@ mod tests { .with_agent("researcher") .with_config_directory(PathBuf::from("/tmp/config")) .with_working_directory(PathBuf::from("/tmp/work")) + .with_additional_directories([PathBuf::from("/tmp/shared")]) .with_github_token("ghp_test") .with_capi(CapiSessionOptions::new().with_enable_web_socket_responses(false)) .with_enable_session_telemetry(false) @@ -6535,6 +6574,10 @@ mod tests { assert_eq!(cfg.agent.as_deref(), Some("researcher")); assert_eq!(cfg.config_directory, Some(PathBuf::from("/tmp/config"))); assert_eq!(cfg.working_directory, Some(PathBuf::from("/tmp/work"))); + assert_eq!( + cfg.additional_directories.as_deref(), + Some(&[PathBuf::from("/tmp/shared")][..]) + ); assert_eq!(cfg.github_token.as_deref(), Some("ghp_test")); assert_eq!( cfg.capi, @@ -6569,6 +6612,29 @@ mod tests { assert!(json.get("continuePendingWork").is_none()); } + #[test] + fn session_configs_serialize_additional_directories() { + let create = SessionConfig::default().with_additional_directories([ + PathBuf::from("/tmp/shared"), + PathBuf::from("/tmp/generated"), + ]); + let (create_wire, _) = create.into_wire(None).expect("no duplicate handlers"); + let create_json = serde_json::to_value(&create_wire).unwrap(); + assert_eq!( + create_json["additionalDirectories"], + serde_json::json!(["/tmp/shared", "/tmp/generated"]) + ); + + let resume = ResumeSessionConfig::new(SessionId::from("sess-1")) + .with_additional_directories([PathBuf::from("/tmp/resumed")]); + let (resume_wire, _) = resume.into_wire().expect("no duplicate handlers"); + let resume_json = serde_json::to_value(&resume_wire).unwrap(); + assert_eq!( + resume_json["additionalDirectories"], + serde_json::json!(["/tmp/resumed"]) + ); + } + /// The Rust field is `suppress_resume_event`, but the wire field stays /// `disableResume` to preserve compatibility with the runtime and other /// SDKs. diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 1620e5ee1..261350e97 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -161,6 +161,8 @@ pub(crate) struct SessionCreateWire { pub config_dir: Option, #[serde(skip_serializing_if = "Option::is_none")] pub working_directory: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_directories: Option>, #[serde(rename = "gitHubToken", skip_serializing_if = "Option::is_none")] pub github_token: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -303,6 +305,8 @@ pub(crate) struct SessionResumeWire { pub config_dir: Option, #[serde(skip_serializing_if = "Option::is_none")] pub working_directory: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_directories: Option>, #[serde(rename = "gitHubToken", skip_serializing_if = "Option::is_none")] pub github_token: Option, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/rust/tests/e2e/support.rs b/rust/tests/e2e/support.rs index 6ad609f58..4d9de5536 100644 --- a/rust/tests/e2e/support.rs +++ b/rust/tests/e2e/support.rs @@ -330,6 +330,9 @@ impl E2eContext { .as_os_str() .to_owned(), ), + ]); + env.extend(isolated_cache_environment(self.home_dir.path())); + env.extend([ ("COPILOT_MCP_APPS".into(), "true".into()), ("MCP_APPS".into(), "true".into()), ("GH_TOKEN".into(), DEFAULT_TEST_TOKEN.into()), @@ -783,6 +786,20 @@ fn canonical_temp_path(path: &Path) -> PathBuf { std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()) } +fn isolated_cache_environment(path: &Path) -> [(OsString, OsString); 2] { + let home_dir = canonical_temp_path(path); + let cache_dir = home_dir.join(".cache"); + // COPILOT_HOME does not redirect platform cache paths, so isolate the cache + // to prevent concurrent CLI processes from sharing mutable startup state. + [ + ( + "COPILOT_CACHE_HOME".into(), + cache_dir.join("copilot").into_os_string(), + ), + ("XDG_CACHE_HOME".into(), cache_dir.into_os_string()), + ] +} + struct CapiProxy { child: Option, proxy_url: String, @@ -1003,3 +1020,25 @@ fn node_program() -> &'static str { fn npx_program() -> &'static str { if cfg!(windows) { "npx.cmd" } else { "npx" } } + +#[test] +fn e2e_context_isolates_copilot_cache() { + let home_dir = tempfile::tempdir().expect("create test home"); + let home_dir = canonical_temp_path(home_dir.path()); + let cache_dir = home_dir.join(".cache"); + let expected = [ + ("COPILOT_CACHE_HOME", cache_dir.join("copilot")), + ("XDG_CACHE_HOME", cache_dir), + ]; + + let environment = isolated_cache_environment(&home_dir); + + for (key, value) in expected { + assert!( + environment.iter().any(|(actual_key, actual_value)| { + actual_key == key && actual_value == value.as_os_str() + }), + "{key} should use the isolated test home" + ); + } +} From bfebe1b6ed53185069579d59f075fdae2a798082 Mon Sep 17 00:00:00 2001 From: examon <1676292+examon@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:37:35 +0200 Subject: [PATCH 12/72] docs: fix the Node.js inbound trace-context example to use a real tool-registration API (#2223) The Node.js example in the "CLI -> SDK (inbound)" section of docs/observability/opentelemetry.md registered a tool by calling session.registerTool(myTool, handler). That method is not part of the public CopilotSession API, so copying the example fails to compile with error TS2339: Property 'registerTool' does not exist on type 'CopilotSession' and, if the types are bypassed, throws "TypeError: session.registerTool is not a function" at runtime. The plural registerTools() is marked @internal and stripped from the shipped declarations, so there is no public session method to register a tool with after the session exists. Move the trace-restoring handler into defineTool() and register the tool through client.createSession({ tools: [myTool] }), which is the public registration path and the one used by the rest of the documentation. The trace-context logic is unchanged; only the registration mechanism is corrected. As a side effect the handler's args and invocation parameters are now contextually typed, so the invocation.traceparent / invocation.tracestate access in the example is actually type-checked. Co-authored-by: examon --- docs/observability/opentelemetry.md | 47 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md index ad86145e4..b3932ce66 100644 --- a/docs/observability/opentelemetry.md +++ b/docs/observability/opentelemetry.md @@ -152,29 +152,36 @@ When the CLI invokes a tool handler, the `traceparent` and `tracestate` from the ```typescript +import { defineTool } from "@github/copilot-sdk"; import { propagation, context, trace } from "@opentelemetry/api"; -session.registerTool(myTool, async (args, invocation) => { - // Restore the CLI's trace context as the active context - const carrier = { - traceparent: invocation.traceparent, - tracestate: invocation.tracestate, - }; - const parentCtx = propagation.extract(context.active(), carrier); - - // Create a child span under the CLI's span - const tracer = trace.getTracer("my-app"); - return context.with(parentCtx, () => - tracer.startActiveSpan("my-tool", async (span) => { - try { - const result = await doWork(args); - return result; - } finally { - span.end(); - } - }) - ); +const myTool = defineTool("my-tool", { + description: "Do work", + handler: async (args, invocation) => { + // Restore the CLI's trace context as the active context + const carrier = { + traceparent: invocation.traceparent, + tracestate: invocation.tracestate, + }; + const parentCtx = propagation.extract(context.active(), carrier); + + // Create a child span under the CLI's span + const tracer = trace.getTracer("my-app"); + return context.with(parentCtx, () => + tracer.startActiveSpan("my-tool", async (span) => { + try { + const result = await doWork(args); + return result; + } finally { + span.end(); + } + }) + ); + }, }); + +// Tool handlers are registered when the session is created. +const session = await client.createSession({ tools: [myTool] }); ``` ### Per-language dependencies From 2e88dbd2b6921c268d4d4ca5812e9aaea9e23f31 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:20:06 -0400 Subject: [PATCH 13/72] Update @github/copilot to 1.0.78 (#2239) - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- dotnet/src/Generated/Rpc.cs | 45 ++++++++++ dotnet/src/Generated/SessionEvents.cs | 31 ++++++- go/rpc/zrpc.go | 61 +++++++++++++ go/rpc/zsession_encoding.go | 6 ++ go/rpc/zsession_events.go | 16 +++- go/zsession_events.go | 2 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 ++++++++-------- java/scripts/codegen/package.json | 2 +- .../generated/SessionContextClearedEvent.java | 43 ++++++++++ .../copilot/generated/SessionEvent.java | 2 + .../generated/SessionModelChangeEvent.java | 2 +- .../generated/rpc/InstalledPlugin.java | 4 +- .../generated/rpc/SessionHistoryApi.java | 16 ++++ .../rpc/SessionHistoryClearContextParams.java | 32 +++++++ .../rpc/SessionHistoryClearContextResult.java | 30 +++++++ .../generated/rpc/SessionInstalledPlugin.java | 4 +- nodejs/package-lock.json | 72 ++++++++-------- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 43 ++++++++++ nodejs/src/generated/session-events.ts | 46 +++++++++- python/copilot/generated/rpc.py | 85 ++++++++++++++++++- python/copilot/generated/session_events.py | 29 ++++++- rust/src/generated/api_types.rs | 53 ++++++++++++ rust/src/generated/rpc.rs | 33 +++++++ rust/src/generated/session_events.rs | 17 +++- test/harness/package-lock.json | 72 ++++++++-------- test/harness/package.json | 2 +- 29 files changed, 702 insertions(+), 124 deletions(-) create mode 100644 java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index dd501e66f..32778850b 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -2988,6 +2988,10 @@ public sealed class InstalledPlugin [JsonPropertyName("source")] public JsonElement? Source { get; set; } + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + [JsonPropertyName("source_sha")] + public string? SourceSha { get; set; } + /// Version installed (if available). [JsonPropertyName("version")] public string? Version { get; set; } @@ -8554,6 +8558,10 @@ public sealed class SessionInstalledPlugin [JsonPropertyName("source")] public JsonElement? Source { get; set; } + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + [JsonPropertyName("source_sha")] + public string? SourceSha { get; set; } + /// Installed version, if known. [JsonPropertyName("version")] public string? Version { get; set; } @@ -13090,6 +13098,28 @@ internal sealed class SessionHistorySummarizeForHandoffRequest public string SessionId { get; set; } = string.Empty; } +/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. +[Experimental(Diagnostics.Experimental)] +public sealed class HistoryClearContextResult +{ + /// Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. + [JsonPropertyName("messagesCleared")] + public long MessagesCleared { get; set; } +} + +/// Parameters for clearing the conversation and seeding the window that replaces it. +[Experimental(Diagnostics.Experimental)] +internal sealed class HistoryClearContextRequest +{ + /// First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// User-facing pending queue entry, with kind and display text for a queued message, slash command, or model change. [Experimental(Diagnostics.Experimental)] public sealed class QueuePendingItems @@ -27662,6 +27692,19 @@ public async Task SummarizeForHandoffAsync(Can var request = new SessionHistorySummarizeForHandoffRequest { SessionId = _session.SessionId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.history.summarizeForHandoff", [request], cancellationToken); } + + /// Clears the session's conversation history, keeping only system and developer messages, and seeds the fresh context window with a first user message. Must be called from inside a tool handler: the clear has to drop the results of the tool calls its wipe orphans, and it rejects when no tool call is in flight. + /// First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. + /// The to monitor for cancellation requests. The default is . + /// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. + public async Task ClearContextAsync(string prompt, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(prompt); + _session.ThrowIfDisposed(); + + var request = new HistoryClearContextRequest { SessionId = _session.SessionId, Prompt = prompt }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.history.clearContext", [request], cancellationToken); + } } /// Provides session-scoped Queue APIs. @@ -29050,6 +29093,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(HandlePendingToolCallResult))] [JsonSerializable(typeof(HistoryAbortManualCompactionResult))] [JsonSerializable(typeof(HistoryCancelBackgroundCompactionResult))] +[JsonSerializable(typeof(HistoryClearContextRequest))] +[JsonSerializable(typeof(HistoryClearContextResult))] [JsonSerializable(typeof(HistoryCompactContextWindow))] [JsonSerializable(typeof(HistoryCompactResult))] [JsonSerializable(typeof(HistoryListRewindPointsResult))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 0f3b53f48..6b81a4770 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -86,6 +86,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(SessionCompactionCompleteEvent), "session.compaction_complete")] [JsonDerivedType(typeof(SessionCompactionStartEvent), "session.compaction_start")] [JsonDerivedType(typeof(SessionContextChangedEvent), "session.context_changed")] +[JsonDerivedType(typeof(SessionContextClearedEvent), "session.context_cleared")] [JsonDerivedType(typeof(SessionCustomAgentsUpdatedEvent), "session.custom_agents_updated")] [JsonDerivedType(typeof(SessionCustomNotificationEvent), "session.custom_notification")] [JsonDerivedType(typeof(SessionErrorEvent), "session.error")] @@ -518,6 +519,19 @@ public sealed partial class SessionUsageInfoEvent : SessionEvent public required SessionUsageInfoData Data { get; set; } } +/// Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages). +/// Represents the session.context_cleared event. +public sealed partial class SessionContextClearedEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "session.context_cleared"; + + /// The session.context_cleared event payload. + [JsonPropertyName("data")] + public required SessionContextClearedData Data { get; set; } +} + /// Context window breakdown at the start of LLM-powered conversation compaction. /// Represents the session.compaction_start event. public sealed partial class SessionCompactionStartEvent : SessionEvent @@ -2029,7 +2043,7 @@ public sealed partial class SessionWarningData /// Model change details including previous and new model identifiers. public sealed partial class SessionModelChangeData { - /// Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. + /// Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("cause")] public string? Cause { get; set; } @@ -2414,6 +2428,19 @@ public sealed partial class SessionUsageInfoData public long? ToolDefinitionsTokens { get; set; } } +/// Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages). +public sealed partial class SessionContextClearedData +{ + /// Optional initial message set after clearing. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("initialMessage")] + public string? InitialMessage { get; set; } + + /// Number of conversation messages that were cleared. + [JsonPropertyName("messagesCleared")] + public required long MessagesCleared { get; set; } +} + /// Context window breakdown at the start of LLM-powered conversation compaction. public sealed partial class SessionCompactionStartData { @@ -13025,6 +13052,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionCompactionStartEvent))] [JsonSerializable(typeof(SessionContextChangedData))] [JsonSerializable(typeof(SessionContextChangedEvent))] +[JsonSerializable(typeof(SessionContextClearedData))] +[JsonSerializable(typeof(SessionContextClearedEvent))] [JsonSerializable(typeof(SessionCustomAgentsUpdatedData))] [JsonSerializable(typeof(SessionCustomAgentsUpdatedEvent))] [JsonSerializable(typeof(SessionCustomNotificationData))] diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index a34dccd2e..5ecee9f6c 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -3038,6 +3038,27 @@ type HistoryCancelBackgroundCompactionResult struct { Cancelled bool `json:"cancelled"` } +// Parameters for clearing the conversation and seeding the window that replaces it. +// Experimental: HistoryClearContextRequest is part of an experimental API and may change or +// be removed. +type HistoryClearContextRequest struct { + // First user message of the fresh context window. Required: a cleared window holding only + // system and developer messages is not a conversation a model can answer, so every clear + // seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop + // exits, which is why the call must be made from inside a tool handler. + Prompt string `json:"prompt"` +} + +// What a successful clear removed. A clear that could not be applied rejects instead of +// reporting a count. +// Experimental: HistoryClearContextResult is part of an experimental API and may change or +// be removed. +type HistoryClearContextResult struct { + // Number of non-system, non-developer messages that were removed from the conversation. + // Zero only when the window already held no conversation. + MessagesCleared int64 `json:"messagesCleared"` +} + // Post-compaction context window usage breakdown // Experimental: HistoryCompactContextWindow is part of an experimental API and may change // or be removed. @@ -3293,6 +3314,12 @@ type InstalledPlugin struct { Name string `json:"name"` // Source for direct repo installs (when marketplace is empty) Source *InstalledPluginSource `json:"source,omitempty"` + // Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus + // its resolved source subtree — NOT a Git commit SHA) captured at marketplace + // install/update time. Auto-update compares it against the freshly recomputed fingerprint + // to detect a content change that does not bump the version. Absent for pre-existing + // installs and for direct (non-marketplace) installs. + SourceSha *string `json:"source_sha,omitempty"` // Version installed (if available) Version *string `json:"version,omitempty"` } @@ -9143,6 +9170,12 @@ type SessionInstalledPlugin struct { Name string `json:"name"` // Source descriptor for direct repo installs (when marketplace is empty) Source *SessionInstalledPluginSource `json:"source,omitempty"` + // Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus + // its resolved source subtree — NOT a Git commit SHA) captured at marketplace + // install/update time. Auto-update compares it against the freshly recomputed fingerprint + // to detect a content change that does not bump the version. Absent for pre-existing + // installs and for direct (non-marketplace) installs. + SourceSha *string `json:"source_sha,omitempty"` // Installed version, if known Version *string `json:"version,omitempty"` } @@ -18201,6 +18234,34 @@ func (a *HistoryAPI) CancelBackgroundCompaction(ctx context.Context) (*HistoryCa return &result, nil } +// ClearContext clears the session's conversation history, keeping only system and developer +// messages, and seeds the fresh context window with a first user message. Must be called +// from inside a tool handler: the clear has to drop the results of the tool calls its wipe +// orphans, and it rejects when no tool call is in flight. +// +// RPC method: session.history.clearContext. +// +// Parameters: Parameters for clearing the conversation and seeding the window that replaces +// it. +// +// Returns: What a successful clear removed. A clear that could not be applied rejects +// instead of reporting a count. +func (a *HistoryAPI) ClearContext(ctx context.Context, params *HistoryClearContextRequest) (*HistoryClearContextResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["prompt"] = params.Prompt + } + raw, err := a.client.Request(ctx, "session.history.clearContext", req) + if err != nil { + return nil, err + } + var result HistoryClearContextResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Compacts the session history to reduce context usage. // // RPC method: session.history.compact. diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index eae934e93..05e466012 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -395,6 +395,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeSessionContextCleared: + var d SessionContextClearedData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeSessionCustomAgentsUpdated: var d SessionCustomAgentsUpdatedData if err := json.Unmarshal(raw.Data, &d); err != nil { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 6855956b9..26e7b5f72 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -131,6 +131,7 @@ const ( SessionEventTypeSessionCompactionComplete SessionEventType = "session.compaction_complete" SessionEventTypeSessionCompactionStart SessionEventType = "session.compaction_start" SessionEventTypeSessionContextChanged SessionEventType = "session.context_changed" + SessionEventTypeSessionContextCleared SessionEventType = "session.context_cleared" SessionEventTypeSessionCustomAgentsUpdated SessionEventType = "session.custom_agents_updated" SessionEventTypeSessionCustomNotification SessionEventType = "session.custom_notification" SessionEventTypeSessionError SessionEventType = "session.error" @@ -402,6 +403,19 @@ func (*SessionCompactionStartData) Type() SessionEventType { return SessionEventTypeSessionCompactionStart } +// Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages) +type SessionContextClearedData struct { + // Optional initial message set after clearing + InitialMessage *string `json:"initialMessage,omitempty"` + // Number of conversation messages that were cleared + MessagesCleared int64 `json:"messagesCleared"` +} + +func (*SessionContextClearedData) sessionEventData() {} +func (*SessionContextClearedData) Type() SessionEventType { + return SessionEventTypeSessionContextCleared +} + // Conversation compaction results including success status, metrics, and optional error details type SessionCompactionCompleteData struct { // Checkpoint snapshot number created for recovery @@ -1004,7 +1018,7 @@ func (*ModelCallStartData) Type() SessionEventType { return SessionEventTypeMode // Model change details including previous and new model identifiers type SessionModelChangeData struct { - // Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. + // Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. Cause *string `json:"cause,omitempty"` // Context tier after the model change; null explicitly clears a previously selected tier ContextTier *ContextTier `json:"contextTier,omitempty"` diff --git a/go/zsession_events.go b/go/zsession_events.go index a6bd1ed73..fbc100277 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -234,6 +234,7 @@ type ( SessionCompactionCompleteData = rpc.SessionCompactionCompleteData SessionCompactionStartData = rpc.SessionCompactionStartData SessionContextChangedData = rpc.SessionContextChangedData + SessionContextClearedData = rpc.SessionContextClearedData SessionCustomAgentsUpdatedData = rpc.SessionCustomAgentsUpdatedData SessionCustomNotificationData = rpc.SessionCustomNotificationData SessionErrorData = rpc.SessionErrorData @@ -626,6 +627,7 @@ const ( SessionEventTypeSessionCompactionComplete = rpc.SessionEventTypeSessionCompactionComplete SessionEventTypeSessionCompactionStart = rpc.SessionEventTypeSessionCompactionStart SessionEventTypeSessionContextChanged = rpc.SessionEventTypeSessionContextChanged + SessionEventTypeSessionContextCleared = rpc.SessionEventTypeSessionContextCleared SessionEventTypeSessionCustomAgentsUpdated = rpc.SessionEventTypeSessionCustomAgentsUpdated SessionEventTypeSessionCustomNotification = rpc.SessionEventTypeSessionCustomNotification SessionEventTypeSessionError = rpc.SessionEventTypeSessionError diff --git a/java/pom.xml b/java/pom.xml index 1e5c76beb..1179adfb0 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.78-2 + ^1.0.78 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index a071da4d7..c4a159b6e 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", - "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", + "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78-2", - "@github/copilot-darwin-x64": "1.0.78-2", - "@github/copilot-linux-arm64": "1.0.78-2", - "@github/copilot-linux-x64": "1.0.78-2", - "@github/copilot-linuxmusl-arm64": "1.0.78-2", - "@github/copilot-linuxmusl-x64": "1.0.78-2", - "@github/copilot-win32-arm64": "1.0.78-2", - "@github/copilot-win32-x64": "1.0.78-2" + "@github/copilot-darwin-arm64": "1.0.78", + "@github/copilot-darwin-x64": "1.0.78", + "@github/copilot-linux-arm64": "1.0.78", + "@github/copilot-linux-x64": "1.0.78", + "@github/copilot-linuxmusl-arm64": "1.0.78", + "@github/copilot-linuxmusl-x64": "1.0.78", + "@github/copilot-win32-arm64": "1.0.78", + "@github/copilot-win32-x64": "1.0.78" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", - "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", + "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", - "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", + "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", - "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", + "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", - "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", + "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", - "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", + "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", - "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", + "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", - "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", + "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", - "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", + "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 0c8287d4c..7547468e8 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java new file mode 100644 index 000000000..7a4e9cd00 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session event "session.context_cleared". Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages) + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionContextClearedEvent extends SessionEvent { + + @Override + public String getType() { return "session.context_cleared"; } + + @JsonProperty("data") + private SessionContextClearedEventData data; + + public SessionContextClearedEventData getData() { return data; } + public void setData(SessionContextClearedEventData data) { this.data = data; } + + /** Data payload for {@link SessionContextClearedEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionContextClearedEventData( + /** Optional initial message set after clearing */ + @JsonProperty("initialMessage") String initialMessage, + /** Number of conversation messages that were cleared */ + @JsonProperty("messagesCleared") Long messagesCleared + ) { + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java index adef027ad..582ecd3d4 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionEvent.java @@ -51,6 +51,7 @@ @JsonSubTypes.Type(value = SessionUsageCheckpointEvent.class, name = "session.usage_checkpoint"), @JsonSubTypes.Type(value = SessionContextChangedEvent.class, name = "session.context_changed"), @JsonSubTypes.Type(value = SessionUsageInfoEvent.class, name = "session.usage_info"), + @JsonSubTypes.Type(value = SessionContextClearedEvent.class, name = "session.context_cleared"), @JsonSubTypes.Type(value = SessionCompactionStartEvent.class, name = "session.compaction_start"), @JsonSubTypes.Type(value = SessionCompactionCompleteEvent.class, name = "session.compaction_complete"), @JsonSubTypes.Type(value = SessionTaskCompleteEvent.class, name = "session.task_complete"), @@ -168,6 +169,7 @@ public abstract sealed class SessionEvent permits SessionUsageCheckpointEvent, SessionContextChangedEvent, SessionUsageInfoEvent, + SessionContextClearedEvent, SessionCompactionStartEvent, SessionCompactionCompleteEvent, SessionTaskCompleteEvent, diff --git a/java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java index f12b86d08..e53c1594a 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java @@ -52,7 +52,7 @@ public record SessionModelChangeEventData( @JsonProperty("verbosity") Verbosity verbosity, /** Context tier after the model change; null explicitly clears a previously selected tier */ @JsonProperty("contextTier") ContextTier contextTier, - /** Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. */ + /** Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. */ @JsonProperty("cause") String cause ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java b/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java index b3487e7e3..3da690f47 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java @@ -34,6 +34,8 @@ public record InstalledPlugin( /** Path where the plugin is cached locally */ @JsonProperty("cache_path") String cachePath, /** Source for direct repo installs (when marketplace is empty) */ - @JsonProperty("source") Object source + @JsonProperty("source") Object source, + /** Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. */ + @JsonProperty("source_sha") String sourceSha ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java index 0d4367f36..ad44d864d 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java @@ -151,4 +151,20 @@ public CompletableFuture summarizeForHa return caller.invoke("session.history.summarizeForHandoff", java.util.Map.of("sessionId", this.sessionId), SessionHistorySummarizeForHandoffResult.class); } + /** + * Parameters for clearing the conversation and seeding the window that replaces it. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture clearContext(SessionHistoryClearContextParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.history.clearContext", _p, SessionHistoryClearContextResult.class); + } + } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java new file mode 100644 index 000000000..e52c27ece --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Parameters for clearing the conversation and seeding the window that replaces it. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionHistoryClearContextParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. */ + @JsonProperty("prompt") String prompt +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java new file mode 100644 index 000000000..4b3d8502c --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionHistoryClearContextResult( + /** Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. */ + @JsonProperty("messagesCleared") Long messagesCleared +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java index ee1bc7154..1109f5f23 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java @@ -34,6 +34,8 @@ public record SessionInstalledPlugin( /** Path where the plugin is cached locally */ @JsonProperty("cache_path") String cachePath, /** Source descriptor for direct repo installs (when marketplace is empty) */ - @JsonProperty("source") Object source + @JsonProperty("source") Object source, + /** Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. */ + @JsonProperty("source_sha") String sourceSha ) { } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index f936a0cbb..0829c3f70 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", - "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", + "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78-2", - "@github/copilot-darwin-x64": "1.0.78-2", - "@github/copilot-linux-arm64": "1.0.78-2", - "@github/copilot-linux-x64": "1.0.78-2", - "@github/copilot-linuxmusl-arm64": "1.0.78-2", - "@github/copilot-linuxmusl-x64": "1.0.78-2", - "@github/copilot-win32-arm64": "1.0.78-2", - "@github/copilot-win32-x64": "1.0.78-2" + "@github/copilot-darwin-arm64": "1.0.78", + "@github/copilot-darwin-x64": "1.0.78", + "@github/copilot-linux-arm64": "1.0.78", + "@github/copilot-linux-x64": "1.0.78", + "@github/copilot-linuxmusl-arm64": "1.0.78", + "@github/copilot-linuxmusl-x64": "1.0.78", + "@github/copilot-win32-arm64": "1.0.78", + "@github/copilot-win32-x64": "1.0.78" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", - "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", + "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", - "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", + "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", - "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", + "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", - "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", + "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", - "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", + "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", - "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", + "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", - "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", + "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", - "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", + "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 92a80eed6..ca259556b 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 4c0e09f2d..aed2d4465 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index b45294a5d..cdd3b29eb 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -6398,6 +6398,32 @@ export interface HistoryCancelBackgroundCompactionResult { */ cancelled: boolean; } +/** + * Parameters for clearing the conversation and seeding the window that replaces it. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "HistoryClearContextRequest". + */ +/** @experimental */ +export interface HistoryClearContextRequest { + /** + * First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. + */ + prompt: string; +} +/** + * What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "HistoryClearContextResult". + */ +/** @experimental */ +export interface HistoryClearContextResult { + /** + * Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. + */ + messagesCleared: number; +} /** * Post-compaction context window usage breakdown * @@ -6735,6 +6761,10 @@ export interface InstalledPlugin { */ cache_path?: string; source?: InstalledPluginSource; + /** + * Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + */ + source_sha?: string; } /** * Source descriptor for a direct GitHub plugin install, with `owner/repo`, optional ref or full commit SHA, and optional subpath. @@ -14269,6 +14299,10 @@ export interface SessionInstalledPlugin { */ cache_path?: string; source?: SessionInstalledPluginSource; + /** + * Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + */ + source_sha?: string; } /** * Source descriptor for a direct GitHub plugin install, with `owner/repo`, optional ref or full commit SHA, and optional subpath. @@ -20912,6 +20946,15 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin */ summarizeForHandoff: async (): Promise => connection.sendRequest("session.history.summarizeForHandoff", { sessionId }), + /** + * Clears the session's conversation history, keeping only system and developer messages, and seeds the fresh context window with a first user message. Must be called from inside a tool handler: the clear has to drop the results of the tool calls its wipe orphans, and it rejects when no tool call is in flight. + * + * @param params Parameters for clearing the conversation and seeding the window that replaces it. + * + * @returns What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. + */ + clearContext: async (params: HistoryClearContextRequest): Promise => + connection.sendRequest("session.history.clearContext", { sessionId, ...params }), }, /** @experimental */ queue: { diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index e6d059ab4..1dad4ab16 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -33,6 +33,7 @@ export type SessionEvent = | UsageCheckpointEvent | ContextChangedEvent | UsageInfoEvent + | ContextClearedEvent | CompactionStartEvent | CompactionCompleteEvent | TaskCompleteEvent @@ -1617,7 +1618,7 @@ export interface ModelChangeEvent { */ export interface ModelChangeData { /** - * Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. + * Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. */ cause?: string; /** @@ -2427,6 +2428,49 @@ export interface UsageInfoData { */ toolDefinitionsTokens?: number; } +/** + * Session event "session.context_cleared". Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages) + */ +export interface ContextClearedEvent { + /** + * Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + */ + agentId?: string; + data: ContextClearedData; + /** + * When true, the event is transient and not persisted to the session event log on disk + */ + ephemeral?: boolean; + /** + * Unique event identifier (UUID v4), generated when the event is emitted + */ + id: string; + /** + * ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + */ + parentId: string | null; + /** + * ISO 8601 timestamp when the event was created + */ + timestamp: string; + /** + * Type discriminator. Always "session.context_cleared". + */ + type: "session.context_cleared"; +} +/** + * Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages) + */ +export interface ContextClearedData { + /** + * Optional initial message set after clearing + */ + initialMessage?: string; + /** + * Number of conversation messages that were cleared + */ + messagesCleared: number; +} /** * Session event "session.compaction_start". Context window breakdown at the start of LLM-powered conversation compaction */ diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index fb9d58675..41e27fc12 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -3008,6 +3008,51 @@ def to_dict(self) -> dict: result["cancelled"] = from_bool(self.cancelled) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class HistoryClearContextRequest: + """Parameters for clearing the conversation and seeding the window that replaces it.""" + + prompt: str + """First user message of the fresh context window. Required: a cleared window holding only + system and developer messages is not a conversation a model can answer, so every clear + seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop + exits, which is why the call must be made from inside a tool handler. + """ + + @staticmethod + def from_dict(obj: Any) -> 'HistoryClearContextRequest': + assert isinstance(obj, dict) + prompt = from_str(obj.get("prompt")) + return HistoryClearContextRequest(prompt) + + def to_dict(self) -> dict: + result: dict = {} + result["prompt"] = from_str(self.prompt) + return result + +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class HistoryClearContextResult: + """What a successful clear removed. A clear that could not be applied rejects instead of + reporting a count. + """ + messages_cleared: int + """Number of non-system, non-developer messages that were removed from the conversation. + Zero only when the window already held no conversation. + """ + + @staticmethod + def from_dict(obj: Any) -> 'HistoryClearContextResult': + assert isinstance(obj, dict) + messages_cleared = from_int(obj.get("messagesCleared")) + return HistoryClearContextResult(messages_cleared) + + def to_dict(self) -> dict: + result: dict = {} + result["messagesCleared"] = from_int(self.messages_cleared) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class HistoryCompactContextWindow: @@ -21925,6 +21970,13 @@ class InstalledPlugin: source: InstalledPluginSource | str | None = None """Source for direct repo installs (when marketplace is empty)""" + source_sha: str | None = None + """Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus + its resolved source subtree — NOT a Git commit SHA) captured at marketplace + install/update time. Auto-update compares it against the freshly recomputed fingerprint + to detect a content change that does not bump the version. Absent for pre-existing + installs and for direct (non-marketplace) installs. + """ version: str | None = None """Version installed (if available)""" @@ -21937,8 +21989,9 @@ def from_dict(obj: Any) -> 'InstalledPlugin': name = from_str(obj.get("name")) cache_path = from_union([from_str, from_none], obj.get("cache_path")) source = from_union([InstalledPluginSource.from_dict, from_str, from_none], obj.get("source")) + source_sha = from_union([from_str, from_none], obj.get("source_sha")) version = from_union([from_str, from_none], obj.get("version")) - return InstalledPlugin(enabled, installed_at, marketplace, name, cache_path, source, version) + return InstalledPlugin(enabled, installed_at, marketplace, name, cache_path, source, source_sha, version) def to_dict(self) -> dict: result: dict = {} @@ -21950,6 +22003,8 @@ def to_dict(self) -> dict: result["cache_path"] = from_union([from_str, from_none], self.cache_path) if self.source is not None: result["source"] = from_union([lambda x: to_class(InstalledPluginSource, x), from_str, from_none], self.source) + if self.source_sha is not None: + result["source_sha"] = from_union([from_str, from_none], self.source_sha) if self.version is not None: result["version"] = from_union([from_str, from_none], self.version) return result @@ -21978,6 +22033,13 @@ class SessionInstalledPlugin: source: SessionInstalledPluginSource | str | None = None """Source descriptor for direct repo installs (when marketplace is empty)""" + source_sha: str | None = None + """Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus + its resolved source subtree — NOT a Git commit SHA) captured at marketplace + install/update time. Auto-update compares it against the freshly recomputed fingerprint + to detect a content change that does not bump the version. Absent for pre-existing + installs and for direct (non-marketplace) installs. + """ version: str | None = None """Installed version, if known""" @@ -21990,8 +22052,9 @@ def from_dict(obj: Any) -> 'SessionInstalledPlugin': name = from_str(obj.get("name")) cache_path = from_union([from_str, from_none], obj.get("cache_path")) source = from_union([SessionInstalledPluginSource.from_dict, from_str, from_none], obj.get("source")) + source_sha = from_union([from_str, from_none], obj.get("source_sha")) version = from_union([from_str, from_none], obj.get("version")) - return SessionInstalledPlugin(enabled, installed_at, marketplace, name, cache_path, source, version) + return SessionInstalledPlugin(enabled, installed_at, marketplace, name, cache_path, source, source_sha, version) def to_dict(self) -> dict: result: dict = {} @@ -22003,6 +22066,8 @@ def to_dict(self) -> dict: result["cache_path"] = from_union([from_str, from_none], self.cache_path) if self.source is not None: result["source"] = from_union([lambda x: to_class(SessionInstalledPluginSource, x), from_str, from_none], self.source) + if self.source_sha is not None: + result["source_sha"] = from_union([from_str, from_none], self.source_sha) if self.version is not None: result["version"] = from_union([from_str, from_none], self.version) return result @@ -28480,6 +28545,8 @@ class RPC: handle_pending_tool_call_result: HandlePendingToolCallResult history_abort_manual_compaction_result: HistoryAbortManualCompactionResult history_cancel_background_compaction_result: HistoryCancelBackgroundCompactionResult + history_clear_context_request: HistoryClearContextRequest + history_clear_context_result: HistoryClearContextResult history_compact_context_window: HistoryCompactContextWindow history_compact_request: Any history_compact_result: HistoryCompactResult @@ -29479,6 +29546,8 @@ def from_dict(obj: Any) -> 'RPC': handle_pending_tool_call_result = HandlePendingToolCallResult.from_dict(obj.get("HandlePendingToolCallResult")) history_abort_manual_compaction_result = HistoryAbortManualCompactionResult.from_dict(obj.get("HistoryAbortManualCompactionResult")) history_cancel_background_compaction_result = HistoryCancelBackgroundCompactionResult.from_dict(obj.get("HistoryCancelBackgroundCompactionResult")) + history_clear_context_request = HistoryClearContextRequest.from_dict(obj.get("HistoryClearContextRequest")) + history_clear_context_result = HistoryClearContextResult.from_dict(obj.get("HistoryClearContextResult")) history_compact_context_window = HistoryCompactContextWindow.from_dict(obj.get("HistoryCompactContextWindow")) history_compact_request = obj.get("HistoryCompactRequest") history_compact_result = HistoryCompactResult.from_dict(obj.get("HistoryCompactResult")) @@ -30268,7 +30337,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -30478,6 +30547,8 @@ def to_dict(self) -> dict: result["HandlePendingToolCallResult"] = to_class(HandlePendingToolCallResult, self.handle_pending_tool_call_result) result["HistoryAbortManualCompactionResult"] = to_class(HistoryAbortManualCompactionResult, self.history_abort_manual_compaction_result) result["HistoryCancelBackgroundCompactionResult"] = to_class(HistoryCancelBackgroundCompactionResult, self.history_cancel_background_compaction_result) + result["HistoryClearContextRequest"] = to_class(HistoryClearContextRequest, self.history_clear_context_request) + result["HistoryClearContextResult"] = to_class(HistoryClearContextResult, self.history_clear_context_result) result["HistoryCompactContextWindow"] = to_class(HistoryCompactContextWindow, self.history_compact_context_window) result["HistoryCompactRequest"] = self.history_compact_request result["HistoryCompactResult"] = to_class(HistoryCompactResult, self.history_compact_result) @@ -33403,6 +33474,12 @@ async def summarize_for_handoff(self, *, timeout: float | None = None) -> Histor "Produces a markdown summary of the session's conversation context for hand-off scenarios.\n\nReturns:\n Markdown summary of the conversation context (empty when not available)." return HistorySummarizeForHandoffResult.from_dict(await self._client.request("session.history.summarizeForHandoff", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + async def clear_context(self, params: HistoryClearContextRequest, *, timeout: float | None = None) -> HistoryClearContextResult: + "Clears the session's conversation history, keeping only system and developer messages, and seeds the fresh context window with a first user message. Must be called from inside a tool handler: the clear has to drop the results of the tool calls its wipe orphans, and it rejects when no tool call is in flight.\n\nArgs:\n params: Parameters for clearing the conversation and seeding the window that replaces it.\n\nReturns:\n What a successful clear removed. A clear that could not be applied rejects instead of reporting a count." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return HistoryClearContextResult.from_dict(await self._client.request("session.history.clearContext", params_dict, **_timeout_kwargs(timeout))) + # Experimental: this API group is experimental and may change or be removed. class QueueApi: @@ -34349,6 +34426,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "HistoryAbortManualCompactionResult", "HistoryApi", "HistoryCancelBackgroundCompactionResult", + "HistoryClearContextRequest", + "HistoryClearContextResult", "HistoryCompactContextWindow", "HistoryCompactRequest", "HistoryCompactResult", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 069a2ae84..6978a5a6d 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -148,6 +148,7 @@ class SessionEventType(Enum): SESSION_USAGE_CHECKPOINT = "session.usage_checkpoint" SESSION_CONTEXT_CHANGED = "session.context_changed" SESSION_USAGE_INFO = "session.usage_info" + SESSION_CONTEXT_CLEARED = "session.context_cleared" SESSION_COMPACTION_START = "session.compaction_start" SESSION_COMPACTION_COMPLETE = "session.compaction_complete" SESSION_TASK_COMPLETE = "session.task_complete" @@ -6267,6 +6268,30 @@ def to_dict(self) -> dict: return result +@dataclass +class SessionContextClearedData: + "Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages)" + messages_cleared: int + initial_message: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "SessionContextClearedData": + assert isinstance(obj, dict) + messages_cleared = from_int(obj.get("messagesCleared")) + initial_message = from_union([from_none, from_str], obj.get("initialMessage")) + return SessionContextClearedData( + messages_cleared=messages_cleared, + initial_message=initial_message, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["messagesCleared"] = to_int(self.messages_cleared) + if self.initial_message is not None: + result["initialMessage"] = from_union([from_none, from_str], self.initial_message) + return result + + @dataclass class SessionCustomAgentsUpdatedData: "Payload of `session.custom_agents_updated` with loaded custom agents plus non-fatal warnings and fatal errors." @@ -10480,7 +10505,7 @@ class WorkspaceFileChangedOperation(Enum): UPDATE = "update" -SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantTurnRetryData | AssistantIntentData | AssistantServerToolProgressData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | ModelCallStartData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | ToolSearchActivatedData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | SessionManagedSettingsResolvedData | SessionManagedSettingsEnforcedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | FactoryRunUpdatedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data +SessionEventData = SessionStartData | SessionResumeData | SessionRemoteSteerableChangedData | SessionErrorData | SessionIdleData | SessionTitleChangedData | SessionScheduleCreatedData | SessionScheduleCancelledData | SessionScheduleRearmedData | SessionAutopilotObjectiveChangedData | SessionInfoData | SessionWarningData | SessionModelChangeData | SessionModeChangedData | SessionSessionLimitsChangedData | SessionPermissionsChangedData | SessionPlanChangedData | SessionTodosChangedData | SessionWorkspaceFileChangedData | SessionHandoffData | SessionTruncationData | SessionSnapshotRewindData | SessionShutdownData | SessionUsageCheckpointData | SessionContextChangedData | SessionUsageInfoData | SessionContextClearedData | SessionCompactionStartData | SessionCompactionCompleteData | SessionTaskCompleteData | UserMessageData | PendingMessagesModifiedData | AssistantTurnStartData | AssistantTurnRetryData | AssistantIntentData | AssistantServerToolProgressData | AssistantReasoningData | AssistantReasoningDeltaData | AssistantToolCallDeltaData | AssistantStreamingDeltaData | AssistantMessageData | AssistantMessageStartData | AssistantMessageDeltaData | AssistantTurnEndData | AssistantIdleData | AssistantUsageData | ModelCallFailureData | ModelCallStartData | AbortData | ToolUserRequestedData | ToolExecutionStartData | ToolExecutionPartialResultData | ToolExecutionProgressData | ToolExecutionCompleteData | ToolSearchActivatedData | SkillInvokedData | SubagentStartedData | SubagentCompletedData | SubagentFailedData | SubagentSelectedData | SubagentDeselectedData | HookStartData | HookEndData | HookProgressData | SessionBinaryAssetData | SystemMessageData | SystemNotificationData | PermissionRequestedData | PermissionCompletedData | UserInputRequestedData | UserInputCompletedData | ElicitationRequestedData | ElicitationCompletedData | SamplingRequestedData | SamplingCompletedData | McpOauthRequiredData | McpOauthCompletedData | McpHeadersRefreshRequiredData | McpHeadersRefreshCompletedData | SessionCustomNotificationData | ExternalToolRequestedData | ExternalToolCompletedData | CommandQueuedData | CommandExecuteData | CommandCompletedData | AutoModeSwitchRequestedData | AutoModeSwitchCompletedData | SessionLimitsExhaustedRequestedData | SessionLimitsExhaustedCompletedData | SessionAutoModeResolvedData | SessionManagedSettingsResolvedData | SessionManagedSettingsEnforcedData | CommandsChangedData | CapabilitiesChangedData | ExitPlanModeRequestedData | ExitPlanModeCompletedData | SessionToolsUpdatedData | SessionBackgroundTasksChangedData | FactoryRunUpdatedData | SessionSkillsLoadedData | SessionCustomAgentsUpdatedData | SessionMcpServersLoadedData | SessionMcpServerStatusChangedData | McpToolsListChangedData | McpResourcesListChangedData | McpPromptsListChangedData | SessionExtensionsLoadedData | SessionCanvasOpenedData | SessionCanvasRegistryChangedData | SessionCanvasClosedData | SessionCanvasUnavailableData | SessionCanvasRecordedData | SessionCanvasRemovedData | SessionExtensionsAttachmentsPushedData | McpAppToolCallCompleteData | RawSessionEventData | Data @dataclass @@ -10532,6 +10557,7 @@ def from_dict(obj: Any) -> "SessionEvent": case SessionEventType.SESSION_USAGE_CHECKPOINT: data = SessionUsageCheckpointData.from_dict(data_obj) case SessionEventType.SESSION_CONTEXT_CHANGED: data = SessionContextChangedData.from_dict(data_obj) case SessionEventType.SESSION_USAGE_INFO: data = SessionUsageInfoData.from_dict(data_obj) + case SessionEventType.SESSION_CONTEXT_CLEARED: data = SessionContextClearedData.from_dict(data_obj) case SessionEventType.SESSION_COMPACTION_START: data = SessionCompactionStartData.from_dict(data_obj) case SessionEventType.SESSION_COMPACTION_COMPLETE: data = SessionCompactionCompleteData.from_dict(data_obj) case SessionEventType.SESSION_TASK_COMPLETE: data = SessionTaskCompleteData.from_dict(data_obj) @@ -10866,6 +10892,7 @@ def session_event_to_dict(x: SessionEvent) -> Any: "SessionCompactionCompleteData", "SessionCompactionStartData", "SessionContextChangedData", + "SessionContextClearedData", "SessionCustomAgentsUpdatedData", "SessionCustomNotificationData", "SessionErrorData", diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index cb0c4da54..b64a13c4a 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -600,6 +600,8 @@ pub mod rpc_methods { pub const SESSION_HISTORY_ABORTMANUALCOMPACTION: &str = "session.history.abortManualCompaction"; /// `session.history.summarizeForHandoff` pub const SESSION_HISTORY_SUMMARIZEFORHANDOFF: &str = "session.history.summarizeForHandoff"; + /// `session.history.clearContext` + pub const SESSION_HISTORY_CLEARCONTEXT: &str = "session.history.clearContext"; /// `session.queue.pendingItems` pub const SESSION_QUEUE_PENDINGITEMS: &str = "session.queue.pendingItems"; /// `session.queue.snapshot` @@ -4948,6 +4950,36 @@ pub struct HistoryCancelBackgroundCompactionResult { pub cancelled: bool, } +/// Parameters for clearing the conversation and seeding the window that replaces it. +/// +///

+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HistoryClearContextRequest { + /// First user message of the fresh context window. Required: a cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. Delivered by the enclosing turn driver once the agentic loop exits, which is why the call must be made from inside a tool handler. + pub prompt: String, +} + +/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct HistoryClearContextResult { + /// Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. + pub messages_cleared: i64, +} + /// Post-compaction context window usage breakdown /// ///
@@ -5308,6 +5340,9 @@ pub struct InstalledPlugin { /// Source for direct repo installs (when marketplace is empty) #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + #[serde(rename = "source_sha", skip_serializing_if = "Option::is_none")] + pub source_sha: Option, /// Version installed (if available) #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, @@ -13531,6 +13566,9 @@ pub struct SessionInstalledPlugin { /// Source descriptor for direct repo installs (when marketplace is empty) #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + #[serde(rename = "source_sha", skip_serializing_if = "Option::is_none")] + pub source_sha: Option, /// Installed version, if known #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, @@ -22493,6 +22531,21 @@ pub struct SessionHistorySummarizeForHandoffResult { pub summary: String, } +/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionHistoryClearContextResult { + /// Number of non-system, non-developer messages that were removed from the conversation. Zero only when the window already held no conversation. + pub messages_cleared: i64, +} + /// Identifies the target session. /// ///
diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 3749b8947..02404c112 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -5087,6 +5087,39 @@ impl<'a> SessionRpcHistory<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Clears the session's conversation history, keeping only system and developer messages, and seeds the fresh context window with a first user message. Must be called from inside a tool handler: the clear has to drop the results of the tool calls its wipe orphans, and it rejects when no tool call is in flight. + /// + /// Wire method: `session.history.clearContext`. + /// + /// # Parameters + /// + /// * `params` - Parameters for clearing the conversation and seeding the window that replaces it. + /// + /// # Returns + /// + /// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn clear_context( + &self, + params: HistoryClearContextRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_HISTORY_CLEARCONTEXT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.instructions.*` RPCs. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index 7ebaa08da..ef3c395b1 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -63,6 +63,8 @@ pub enum SessionEventType { SessionContextChanged, #[serde(rename = "session.usage_info")] SessionUsageInfo, + #[serde(rename = "session.context_cleared")] + SessionContextCleared, #[serde(rename = "session.compaction_start")] SessionCompactionStart, #[serde(rename = "session.compaction_complete")] @@ -380,6 +382,8 @@ pub enum SessionEventData { SessionContextChanged(SessionContextChangedData), #[serde(rename = "session.usage_info")] SessionUsageInfo(SessionUsageInfoData), + #[serde(rename = "session.context_cleared")] + SessionContextCleared(SessionContextClearedData), #[serde(rename = "session.compaction_start")] SessionCompactionStart(SessionCompactionStartData), #[serde(rename = "session.compaction_complete")] @@ -964,7 +968,7 @@ pub struct SessionWarningData { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionModelChangeData { - /// Reason the change happened, when not user-initiated. Currently `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path. UI clients can use this to render contextual copy. + /// Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. #[serde(skip_serializing_if = "Option::is_none")] pub cause: Option, /// Context tier after the model change; null explicitly clears a previously selected tier @@ -1369,6 +1373,17 @@ pub struct SessionUsageInfoData { pub tool_definitions_tokens: Option, } +/// Session event "session.context_cleared". Context-cleared details emitted when the host clears the conversation (the session.history.clearContext RPC / Session.clearContextMessages) +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionContextClearedData { + /// Optional initial message set after clearing + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_message: Option, + /// Number of conversation messages that were cleared + pub messages_cleared: i64, +} + /// Session event "session.compaction_start". Context window breakdown at the start of LLM-powered conversation compaction #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 4cfbf7b85..da6e36ffb 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78-2.tgz", - "integrity": "sha512-9MrssRFvYWFPnePZ8BFgMGv735awQ19KrKMZYr14u7Kp9j8l3jyUiSMKa5oCJD5V0mR52+1YE2jy4TCfF/mqlA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", + "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78-2", - "@github/copilot-darwin-x64": "1.0.78-2", - "@github/copilot-linux-arm64": "1.0.78-2", - "@github/copilot-linux-x64": "1.0.78-2", - "@github/copilot-linuxmusl-arm64": "1.0.78-2", - "@github/copilot-linuxmusl-x64": "1.0.78-2", - "@github/copilot-win32-arm64": "1.0.78-2", - "@github/copilot-win32-x64": "1.0.78-2" + "@github/copilot-darwin-arm64": "1.0.78", + "@github/copilot-darwin-x64": "1.0.78", + "@github/copilot-linux-arm64": "1.0.78", + "@github/copilot-linux-x64": "1.0.78", + "@github/copilot-linuxmusl-arm64": "1.0.78", + "@github/copilot-linuxmusl-x64": "1.0.78", + "@github/copilot-win32-arm64": "1.0.78", + "@github/copilot-win32-x64": "1.0.78" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78-2.tgz", - "integrity": "sha512-tZ+53pbjdFzyIJHBhRhjbKTZZjBT2gJ2RF+MRqJKE9uv774rNxtWb3a9T3Yfu78smjusKjFf0dfJp2rabhxAKQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", + "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78-2.tgz", - "integrity": "sha512-DpCSlK8u+k5bHeLpbYjJpDaIMlPulpDoTi5zJOcmKIBcxBUx5/RjPogq6DjumNSyLC09Fm9ddMPQgitvMxhDUw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", + "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78-2.tgz", - "integrity": "sha512-ERhA6MoAL3yYRdYXN6IielJ8MJheSq7AnchCG92LWq7bRuFwmdQf0mQZzhZd/W6xHsistAQ9dByeVH/UAFB5mA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", + "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78-2.tgz", - "integrity": "sha512-lbHfY2NrgPxhpJTnvbMWmtxDwSwkgIb9wqJGCQ50ofeX4RuONmBb1960rtmkECtGyN6zDUzIatX7MNBRRBFIpA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", + "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78-2.tgz", - "integrity": "sha512-xXHza3RpX/RbTY7/DDK8Bt7kDU0pDEn1Nf1T08X9o06FhUjTeh7wTMUh/Ogfq9ocG5K4v8fuk1ONv63viQVeIA==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", + "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78-2.tgz", - "integrity": "sha512-ihWyNGlyJHs1iAZsG+BLYzRxpKzRX+OV7E5HVIlAAxL6fxw2ymkgrfSAfx5FR8D2ZXWloY14ZKttFUbizAyJkg==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", + "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78-2.tgz", - "integrity": "sha512-5rzE6ysT8ZMCZ8zhcgtExaaZ05UFo6KOCQZNYi+YGx8xpObR92vruZKKhBZH2vE51DM47dT1JQ9o0jS6eP7/dw==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", + "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78-2.tgz", - "integrity": "sha512-10bTnLdDXiLcjWTMAoEmSuqkmf8Q+no9B5pL3u5ks3WiFLGx/r9oDo8CeyYV965CEsfoRkBIxWaLUTMra1tlxQ==", + "version": "1.0.78", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", + "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index df0a1b968..f1556534d 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.78-2", + "@github/copilot": "^1.0.78", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From 08df36387c411046d70e9ece45c6462913cc4bbc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 01:52:42 +0000 Subject: [PATCH 14/72] docs: update version references to 1.0.9-preview.3 --- java/README.md | 6 +++--- java/jbang-example.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/README.md b/java/README.md index 1737d82d4..1ff1c90c7 100644 --- a/java/README.md +++ b/java/README.md @@ -39,7 +39,7 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.9-preview.2-01' +implementation 'com.github:copilot-sdk-java:1.0.9-preview.3-01' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.10-preview.2-SNAPSHOT + 1.0.10-preview.3-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.10-preview.2-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.10-preview.3-SNAPSHOT' ``` ## Quick Start diff --git a/java/jbang-example.java b/java/jbang-example.java index ac76d7652..59e625724 100644 --- a/java/jbang-example.java +++ b/java/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.9-preview.2-01 +//DEPS com.github:copilot-sdk-java:1.0.9-preview.3-01 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From 52f0eb710fc0b8cf8027b78458d0077baaac2b57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 01:53:12 +0000 Subject: [PATCH 15/72] [maven-release-plugin] prepare release java/v1.0.9-preview.3 --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 1179adfb0..fe3d443d7 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.10-preview.2-SNAPSHOT + 1.0.9-preview.3 jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.9-preview.3 From f324f722751e0360573e3f1d572c3d44d44b8709 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 4 Aug 2026 01:53:16 +0000 Subject: [PATCH 16/72] [maven-release-plugin] prepare for next development iteration --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index fe3d443d7..efb54bbef 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.9-preview.3 + 1.0.10-preview.3-SNAPSHOT jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.9-preview.3 + HEAD From a540e9c1132e6bfde4689ce8c42bbe6d65d77635 Mon Sep 17 00:00:00 2001 From: HindzSight Date: Tue, 4 Aug 2026 16:21:20 +0530 Subject: [PATCH 17/72] dotnet: update README attachment examples to use AttachmentFile/AttachmentBlob; fix Session example (fixes #2196 (#2208) --- dotnet/README.md | 15 +++++++-------- dotnet/src/Session.cs | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/dotnet/README.md b/dotnet/README.md index 1971c2107..8c0e01e9e 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -296,9 +296,9 @@ The SDK supports image attachments via the `Attachments` parameter. You can atta await session.SendAsync(new MessageOptions { Prompt = "What's in this image?", - Attachments = new List + Attachments = new List { - new UserMessageDataAttachmentsItemFile + new AttachmentFile { Path = "/path/to/image.jpg", DisplayName = "image.jpg", @@ -310,9 +310,9 @@ await session.SendAsync(new MessageOptions await session.SendAsync(new MessageOptions { Prompt = "What's in this image?", - Attachments = new List + Attachments = new List { - new UserMessageDataAttachmentsItemBlob + new AttachmentBlob { Data = base64ImageData, MimeType = "image/png", @@ -721,13 +721,12 @@ await session2.SendAsync(new MessageOptions { Prompt = "Hello from session 2" }) await session.SendAsync(new MessageOptions { Prompt = "Analyze this file", - Attachments = new List + Attachments = new List { - new UserMessageDataAttachmentsItem + new AttachmentFile { - Type = UserMessageDataAttachmentsItemType.File, Path = "/path/to/file.cs", - DisplayName = "My File" + DisplayName = "My File", } } }); diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index d2f6cedbf..1105d4534 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -1,4 +1,4 @@ -/*--------------------------------------------------------------------------------------------- +/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ @@ -263,7 +263,7 @@ public Task SendAsync(string prompt, CancellationToken cancellationToken /// Prompt = "Explain this code", /// Attachments = new List<Attachment> /// { - /// new() { Type = "file", Path = "./Program.cs" } + /// new AttachmentFile { Path = "./Program.cs", DisplayName = "Program.cs" } /// } /// }); /// From 483324d6e93492a4b4f4134a897d712d415f316b Mon Sep 17 00:00:00 2001 From: Dharshika Date: Tue, 4 Aug 2026 16:44:36 +0530 Subject: [PATCH 18/72] Support reasoningEffort: max (#2228) * feat: support reasoningEffort 'max' in SDK types and docs * Fix Java formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/README.md | 2 +- dotnet/src/Session.cs | 2 +- dotnet/src/Types.cs | 2 +- go/README.md | 2 +- go/session.go | 2 +- go/types.go | 4 ++-- java/src/main/java/com/github/copilot/CopilotSession.java | 6 ++++-- .../java/com/github/copilot/rpc/ResumeSessionConfig.java | 5 +++-- .../main/java/com/github/copilot/rpc/SessionConfig.java | 7 ++++--- nodejs/README.md | 2 +- nodejs/src/types.ts | 2 +- python/README.md | 2 +- python/copilot/session.py | 4 ++-- rust/src/types.rs | 2 +- 14 files changed, 24 insertions(+), 20 deletions(-) diff --git a/dotnet/README.md b/dotnet/README.md index 8c0e01e9e..cca93442e 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -123,7 +123,7 @@ Create a new conversation session. - `SessionId` - Custom session ID - `Model` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.) -- `ReasoningEffort` - Reasoning effort level for models that support it ("low", "medium", "high", "xhigh"). Use `ListModelsAsync()` to check which models support this option. +- `ReasoningEffort` - Reasoning effort level for models that support it ("low", "medium", "high", "xhigh", "max"). Use `ListModelsAsync()` to check which models support this option. - `Tools` - Custom tool declarations exposed to the CLI. Declarations without an invocable `AIFunction` are left pending for manual resolution. - `SystemMessage` - System message customization - `AvailableTools` - List of tool names to allow diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index 1105d4534..e3bd56af9 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -1790,7 +1790,7 @@ public async Task AbortAsync(CancellationToken cancellationToken = default) /// The new model takes effect for the next message. Conversation history is preserved. ///
/// Model ID to switch to (e.g., "gpt-5.4"). - /// Reasoning effort level (e.g., "low", "medium", "high", "xhigh"). + /// Reasoning effort level (e.g., "low", "medium", "high", "xhigh", "max"). /// Per-property overrides for model capabilities, deep-merged over runtime defaults. /// Optional cancellation token. /// diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index e1ba0355f..1f64b6f9d 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3110,7 +3110,7 @@ protected SessionConfigBase(SessionConfigBase? other) /// /// Reasoning effort level for models that support it. - /// Valid values: "low", "medium", "high", "xhigh". + /// Valid values: "low", "medium", "high", "xhigh", "max". /// Only applies to models where capabilities.supports.reasoningEffort is true. /// public string? ReasoningEffort { get; set; } diff --git a/go/README.md b/go/README.md index bc360253f..692203614 100644 --- a/go/README.md +++ b/go/README.md @@ -210,7 +210,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec **SessionConfig:** - `Model` (string): Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.** -- `ReasoningEffort` (string): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh"). Use `ListModels()` to check which models support this option. +- `ReasoningEffort` (string): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh", "max"). Use `ListModels()` to check which models support this option. - `SessionID` (string): Custom session ID - `Tools` ([]Tool): Custom tools exposed to the CLI - `SystemMessage` (\*SystemMessageConfig): System message configuration. Supports three modes: diff --git a/go/session.go b/go/session.go index a04119720..a30700ce1 100644 --- a/go/session.go +++ b/go/session.go @@ -1769,7 +1769,7 @@ func (s *Session) Abort(ctx context.Context) error { // SetModelOptions configures optional parameters for SetModel. type SetModelOptions struct { - // ReasoningEffort sets the reasoning effort level for the new model (e.g., "low", "medium", "high", "xhigh"). + // ReasoningEffort sets the reasoning effort level for the new model (e.g., "low", "medium", "high", "xhigh", "max"). ReasoningEffort *string // ReasoningSummary sets the reasoning summary mode for the new model. // Use ReasoningSummaryNone to suppress summary output regardless of whether reasoning is enabled. diff --git a/go/types.go b/go/types.go index 3ddcf930d..d9e92ef50 100644 --- a/go/types.go +++ b/go/types.go @@ -1163,7 +1163,7 @@ type SessionConfig struct { // Model to use for this session Model string // ReasoningEffort level for models that support it. - // Valid values: "low", "medium", "high", "xhigh" + // Valid values: "low", "medium", "high", "xhigh", "max" // Only applies to models where capabilities.supports.reasoningEffort is true. ReasoningEffort string // ReasoningSummary mode for models that support configurable reasoning summaries. @@ -1721,7 +1721,7 @@ type ResumeSessionConfig struct { // Only non-nil fields are applied over the runtime-resolved capabilities. ModelCapabilities *rpc.ModelCapabilitiesOverride // ReasoningEffort level for models that support it. - // Valid values: "low", "medium", "high", "xhigh" + // Valid values: "low", "medium", "high", "xhigh", "max" ReasoningEffort string // ReasoningSummary mode for models that support configurable reasoning summaries. // Use ReasoningSummaryNone to suppress summary output regardless of whether reasoning is enabled. diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/src/main/java/com/github/copilot/CopilotSession.java index aacd9ee48..cb16f55fc 100644 --- a/java/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/src/main/java/com/github/copilot/CopilotSession.java @@ -1972,7 +1972,8 @@ public CompletableFuture abort() { * the model ID to switch to (e.g., {@code "gpt-5.4"}) * @param reasoningEffort * reasoning effort level (e.g., {@code "low"}, {@code "medium"}, - * {@code "high"}, {@code "xhigh"}); {@code null} to use default + * {@code "high"}, {@code "xhigh"}, {@code "max"}); {@code null} to + * use default * @return a future that completes when the model switch is acknowledged * @throws IllegalStateException * if this session has been terminated @@ -2003,7 +2004,8 @@ public CompletableFuture setModel(String model, String reasoningEffort) { * the model ID to switch to (e.g., {@code "gpt-5.4"}) * @param reasoningEffort * reasoning effort level (e.g., {@code "low"}, {@code "medium"}, - * {@code "high"}, {@code "xhigh"}); {@code null} to use default + * {@code "high"}, {@code "xhigh"}, {@code "max"}); {@code null} to + * use default * @param modelCapabilities * per-property overrides for model capabilities; {@code null} to use * runtime defaults diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 35072f129..05b73e88a 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -685,7 +685,8 @@ public ResumeSessionConfig clearManageScheduleEnabled() { /** * Gets the reasoning effort level. * - * @return the reasoning effort level ("low", "medium", "high", or "xhigh") + * @return the reasoning effort level ("low", "medium", "high", "xhigh", or + * "max") */ public String getReasoningEffort() { return reasoningEffort; @@ -694,7 +695,7 @@ public String getReasoningEffort() { /** * Sets the reasoning effort level for models that support it. *

- * Valid values: "low", "medium", "high", "xhigh". + * Valid values: "low", "medium", "high", "xhigh", "max". * * @param reasoningEffort * the reasoning effort level diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index c062b1c7c..e36a02793 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -180,7 +180,8 @@ public SessionConfig setModel(String model) { /** * Gets the reasoning effort level. * - * @return the reasoning effort level ("low", "medium", "high", or "xhigh") + * @return the reasoning effort level ("low", "medium", "high", "xhigh", or + * "max") */ public String getReasoningEffort() { return reasoningEffort; @@ -189,8 +190,8 @@ public String getReasoningEffort() { /** * Sets the reasoning effort level for models that support it. *

- * Valid values: "low", "medium", "high", "xhigh". Only applies to models where - * {@code capabilities.supports.reasoningEffort} is true. + * Valid values: "low", "medium", "high", "xhigh", "max". Only applies to models + * where {@code capabilities.supports.reasoningEffort} is true. * * @param reasoningEffort * the reasoning effort level diff --git a/nodejs/README.md b/nodejs/README.md index 4c430da05..416d9bb0d 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -131,7 +131,7 @@ Create a new conversation session. - `sessionId?: string` - Custom session ID. - `model?: string` - Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.** -- `reasoningEffort?: "low" | "medium" | "high" | "xhigh"` - Reasoning effort level for models that support it. Use `listModels()` to check which models support this option. +- `reasoningEffort?: "low" | "medium" | "high" | "xhigh" | "max"` - Reasoning effort level for models that support it. Use `listModels()` to check which models support this option. - `tools?: Tool[]` - Custom tools exposed to the CLI. Tools without `handler` are declaration-only and must be resolved via pending tool-call RPCs. - `systemMessage?: SystemMessageConfig` - System message customization (see below) - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below) diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index a8a9410f8..0c15d2df4 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1823,7 +1823,7 @@ export interface LargeToolOutputConfig { /** * Valid reasoning effort levels for models that support it. */ -export type ReasoningEffort = "low" | "medium" | "high" | "xhigh"; +export type ReasoningEffort = "low" | "medium" | "high" | "xhigh" | "max"; /** * Context window tier for the session. "long_context" pins the session to the diff --git a/python/README.md b/python/README.md index 0206ad49b..8fdb1f191 100644 --- a/python/README.md +++ b/python/README.md @@ -272,7 +272,7 @@ finally: These are passed as keyword arguments to `create_session()`: - `model` (str): Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.** -- `reasoning_effort` (str): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh"). Use `list_models()` to check which models support this option. +- `reasoning_effort` (str): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh", "max"). Use `list_models()` to check which models support this option. - `session_id` (str): Custom session ID - `tools` (list): Custom tools exposed to the CLI. Tools with `handler=None` are declaration-only and must be resolved via pending tool-call RPCs. - `system_message` (SystemMessageConfig): System message configuration diff --git a/python/copilot/session.py b/python/copilot/session.py index 4474ce562..193c571b3 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -169,7 +169,7 @@ def _capabilities_to_dict(caps: ModelCapabilitiesOverride) -> dict: return result -ReasoningEffort = Literal["low", "medium", "high", "xhigh"] +ReasoningEffort = Literal["low", "medium", "high", "xhigh", "max"] ReasoningSummary = Literal["none", "concise", "detailed"] ContextTier = Literal["default", "long_context"] SessionFsConventions = Literal["posix", "windows"] @@ -2976,7 +2976,7 @@ async def set_model( Args: model: Model ID to switch to (e.g., "gpt-5.4", "claude-sonnet-4"). reasoning_effort: Optional reasoning effort level for the new model - (e.g., "low", "medium", "high", "xhigh"). + (e.g., "low", "medium", "high", "xhigh", "max"). reasoning_summary: Optional reasoning summary mode for supported models. Use "none" to suppress summary output regardless of whether reasoning is enabled. diff --git a/rust/src/types.rs b/rust/src/types.rs index d2b8dcb93..57497cb70 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -4391,7 +4391,7 @@ impl LogOptions { #[derive(Debug, Clone, Default)] pub struct SetModelOptions { /// Reasoning effort for the new model (e.g. `"low"`, `"medium"`, - /// `"high"`, `"xhigh"`). + /// `"high"`, `"xhigh"`, `"max"`). pub reasoning_effort: Option, /// Reasoning summary mode for the new model. Use /// [`ReasoningSummary::None`] to suppress summary output regardless of From 98c68340b2f73ea820cce0c84dab8b1d5f8be2c9 Mon Sep 17 00:00:00 2001 From: Thejesh <35212698+thejesh23@users.noreply.github.com> Date: Tue, 4 Aug 2026 04:57:30 -0700 Subject: [PATCH 19/72] Stop sendAndWait from emitting an unhandled rejection (#2206) * Stop sendAndWait from emitting an unhandled rejection `sendAndWait` creates `idlePromise` and registers the event listener that can reject it, but the first consumer is only attached by the `Promise.race` further down -- after `await this.send(options)`, a full JSON-RPC round trip to the CLI. A `session.error` arriving in that window therefore rejects a promise that has no handler yet. Node's rejection tracker runs at the following checkpoint, well before the `session.send` response lands, and classifies it as unhandled, which terminates the process under the default `--unhandled-rejections=throw`. The window is reachable from ordinary, non-fatal traffic. `session.log` with `{ level: "error" }` emits a `session.error` carrying `errorType: "notification"` (asserted in test/e2e/session.e2e.test.ts), so a joined client or extension writing an error log line while another caller is mid-`sendAndWait` is enough. MCP servers failing to start and sub-agent errors do the same. A caller cannot defend against this: the rejection is on the internal promise, not on the one `sendAndWait` returns, so even correct `.catch`/`try` handling around the call does not prevent the crash. Attaching a no-op `catch` marks the promise handled without consuming the rejection, so the `Promise.race` still observes it and `sendAndWait` rejects with the original error exactly as before. The added test drives a session whose `session.send` RPC is held open, dispatches a `session.error` into the window, and asserts both that no `unhandledRejection` fires and that `sendAndWait` still rejects. It fails on the unfixed code with the error captured by the process-level listener. * Model sendAndWait completion without rejected promise Represent idle and error events as a resolved outcome so an error received while session.send is pending cannot become an unhandled rejection. Preserve send failure precedence and first-event settlement, with focused ordering tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: thejesh23 Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- nodejs/src/session.ts | 18 +-- nodejs/test/session-send-and-wait.test.ts | 137 ++++++++++++++++++++++ 2 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 nodejs/test/session-send-and-wait.test.ts diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index e0a3df0e6..5985d1763 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -734,11 +734,10 @@ export class CopilotSession { typeof optionsOrPrompt === "string" ? { prompt: optionsOrPrompt } : optionsOrPrompt; const effectiveTimeout = timeout ?? 60_000; - let resolveIdle: () => void; - let rejectWithError: (error: Error) => void; - const idlePromise = new Promise((resolve, reject) => { - resolveIdle = resolve; - rejectWithError = reject; + type SessionOutcome = { kind: "idle" } | { kind: "error"; error: Error }; + let resolveOutcome: (outcome: SessionOutcome) => void; + const outcomePromise = new Promise((resolve) => { + resolveOutcome = resolve; }); let lastAssistantMessage: AssistantMessageEvent | undefined; @@ -749,11 +748,11 @@ export class CopilotSession { if (event.type === "assistant.message") { lastAssistantMessage = event; } else if (event.type === "session.idle") { - resolveIdle(); + resolveOutcome({ kind: "idle" }); } else if (event.type === "session.error") { const error = new Error(event.data.message); error.stack = event.data.stack; - rejectWithError(error); + resolveOutcome({ kind: "error", error }); } }); @@ -772,7 +771,10 @@ export class CopilotSession { effectiveTimeout ); }); - await Promise.race([idlePromise, timeoutPromise]); + const outcome = await Promise.race([outcomePromise, timeoutPromise]); + if (outcome.kind === "error") { + throw outcome.error; + } return lastAssistantMessage; } finally { diff --git a/nodejs/test/session-send-and-wait.test.ts b/nodejs/test/session-send-and-wait.test.ts new file mode 100644 index 000000000..8b6e390c4 --- /dev/null +++ b/nodejs/test/session-send-and-wait.test.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { describe, expect, it, onTestFinished } from "vitest"; +import type { MessageConnection } from "vscode-jsonrpc/node.js"; +import { CopilotSession } from "../src/session.js"; +import type { SessionEvent } from "../src/generated/session-events.js"; + +function sessionEvent(type: "session.idle", data: Record = {}): SessionEvent { + return { + type, + id: "00000000-0000-4000-8000-000000000001", + parentId: null, + timestamp: new Date().toISOString(), + ephemeral: true, + data, + } as SessionEvent; +} + +/** Builds a `session.error` event, the shape `session.log(…, { level: "error" })` produces. */ +function errorEvent(message: string): SessionEvent { + return { + type: "session.error", + id: "00000000-0000-4000-8000-000000000001", + parentId: null, + timestamp: new Date().toISOString(), + data: { errorType: "notification", message }, + } as SessionEvent; +} + +function controlledSession(): { + session: CopilotSession; + sendStarted: Promise; + resolveSend: () => void; + rejectSend: (error: Error) => void; +} { + let resolveSendRequest: ((value: unknown) => void) | undefined; + let rejectSendRequest: ((error: Error) => void) | undefined; + let markSendStarted: () => void; + const sendStarted = new Promise((resolve) => { + markSendStarted = resolve; + }); + const connection = { + sendRequest: () => + new Promise((resolve, reject) => { + resolveSendRequest = resolve; + rejectSendRequest = reject; + markSendStarted(); + }), + } as unknown as MessageConnection; + + return { + session: new CopilotSession("session-1", connection), + sendStarted, + resolveSend: () => resolveSendRequest?.({ messageId: "msg-1" }), + rejectSend: (error) => rejectSendRequest?.(error), + }; +} + +describe("sendAndWait", () => { + it("does not emit an unhandled rejection when session.error arrives before the idle race is armed", async () => { + const { session, sendStarted, resolveSend } = controlledSession(); + + const unhandled: unknown[] = []; + const onUnhandled = (reason: unknown): void => { + unhandled.push(reason); + }; + process.on("unhandledRejection", onUnhandled); + onTestFinished(() => { + process.off("unhandledRejection", onUnhandled); + }); + + const pending = session.sendAndWait({ prompt: "hi" }); + await sendStarted; + + // A session.error lands while send()'s RPC is still in flight. This is + // ordinary traffic: a joined client calling session.log(…, { level: "error" }) + // or an MCP server failing to start both produce one. + session._dispatchEvent(errorEvent("MCP server failed to start")); + + // Yield past a macrotask boundary so Node has run the checkpoint at which + // it classifies a rejection as unhandled. + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(unhandled).toEqual([]); + + resolveSend(); + await expect(pending).rejects.toThrow("MCP server failed to start"); + }); + + it("preserves an early idle event until send completes", async () => { + const { session, sendStarted, resolveSend } = controlledSession(); + const pending = session.sendAndWait({ prompt: "hi" }); + await sendStarted; + + session._dispatchEvent(sessionEvent("session.idle")); + + const stateBeforeSend = await Promise.race([ + pending.then(() => "settled"), + new Promise<"pending">((resolve) => setTimeout(() => resolve("pending"), 0)), + ]); + expect(stateBeforeSend).toBe("pending"); + + resolveSend(); + await expect(pending).resolves.toBeUndefined(); + }); + + it("preserves the send rejection when a session error arrives first", async () => { + const { session, sendStarted, rejectSend } = controlledSession(); + const pending = session.sendAndWait({ prompt: "hi" }); + await sendStarted; + + session._dispatchEvent(errorEvent("session error")); + rejectSend(new Error("send failed")); + + await expect(pending).rejects.toThrow("send failed"); + }); + + it("uses the first session outcome observed while send is in flight", async () => { + const idleFirst = controlledSession(); + const idleFirstPending = idleFirst.session.sendAndWait({ prompt: "hi" }); + await idleFirst.sendStarted; + idleFirst.session._dispatchEvent(sessionEvent("session.idle")); + idleFirst.session._dispatchEvent(errorEvent("later error")); + idleFirst.resolveSend(); + await expect(idleFirstPending).resolves.toBeUndefined(); + + const errorFirst = controlledSession(); + const errorFirstPending = errorFirst.session.sendAndWait({ prompt: "hi" }); + await errorFirst.sendStarted; + errorFirst.session._dispatchEvent(errorEvent("first error")); + errorFirst.session._dispatchEvent(sessionEvent("session.idle")); + errorFirst.resolveSend(); + await expect(errorFirstPending).rejects.toThrow("first error"); + }); +}); From a76c968f5a7db04b3d7c3853a27938f2bde14f5b Mon Sep 17 00:00:00 2001 From: Mark Xian Date: Tue, 4 Aug 2026 22:07:13 +0800 Subject: [PATCH 20/72] docs: clarify working directory defaults across SDKs (#2201) * docs(dotnet): clarify working directory fallback * docs: align working directory defaults across SDKs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/README.md | 3 ++- go/README.md | 3 ++- java/README.md | 2 ++ nodejs/README.md | 1 + python/README.md | 1 + rust/README.md | 2 ++ 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dotnet/README.md b/dotnet/README.md index cca93442e..8f987893f 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -84,7 +84,7 @@ new CopilotClient(CopilotClientOptions? options = null) - `Connection` - How to connect to the Copilot runtime. Defaults to `null` (equivalent to `RuntimeConnection.ForStdio()` with the bundled runtime). See "RuntimeConnection" below. - `LogLevel` - Runtime log level. Accepts well-known values `CopilotLogLevel.None`, `Error`, `Warning`, `Info`, `Debug`, `All`. Defaults to null (the runtime's own default). -- `WorkingDirectory` - Working directory for the runtime process. +- `WorkingDirectory` - Working directory for the runtime process. When not set, the spawned runtime inherits the calling application's current working directory. - `BaseDirectory` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime process. When not set, the runtime defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when connecting via `RuntimeConnection.ForUri(...)`. - `EnableRemoteSessions` - Enables remote-session features. - `Environment` - Environment variables to pass to the runtime process. @@ -131,6 +131,7 @@ Create a new conversation session. - `Provider` - Custom API provider configuration (BYOK) - `Streaming` - Enable streaming of response chunks (default: false) - `InfiniteSessions` - Configure automatic context compaction (see below) +- `WorkingDirectory` - Working directory for the session. When not set, the runtime uses its own process working directory. - `EnableSessionStore` - Enables the cross-session store for search and retrieval across sessions. When unset in `CopilotClientMode.CopilotCli`, the runtime default applies (enabled). In `CopilotClientMode.Empty`, defaults to disabled. - `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.ApproveAll` approves requests when managed settings are disabled and throws when `EnableManagedSettings` is true. Custom handlers can inspect `ManagedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` - Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. diff --git a/go/README.md b/go/README.md index 692203614..16053c1ab 100644 --- a/go/README.md +++ b/go/README.md @@ -198,7 +198,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec When `Path` is empty for stdio/tcp, the SDK uses the bundled CLI (or `COPILOT_CLI_PATH` env var). `StdioConnection` and `TCPConnection` accept an optional connection-level `Env`. Set environment variables via **either** the client-level `Env` option or the connection's `Env`, not both (setting both panics); prefer the connection-level `Env`. -- `WorkingDirectory` (string): Working directory for the runtime process +- `WorkingDirectory` (string): Working directory for the runtime process (default: current process working directory) - `BaseDirectory` (string): Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When empty, the runtime defaults to `~/.copilot`. Ignored with `URIConnection`. This does **not** affect where the Go SDK extracts the embedded CLI binary; use `embeddedcli.Config.Dir` for the extraction/cache location. - `LogLevel` (string): Log level. When empty (default), the runtime uses its own default level (the SDK does not pass `--log-level`). - `Env` ([]string): Environment variables for the runtime process (default: inherits from current process) @@ -220,6 +220,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `Provider` (\*ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. - `Streaming` (*bool): Enable streaming delta events (nil = runtime default) - `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration +- `WorkingDirectory` (string): Working directory for the session (default: runtime process working directory) - `EnableSessionStore` (\*bool): Enables the cross-session store for search and retrieval across sessions. When unset in `ModeCopilotCli`, the runtime default applies (enabled). In `ModeEmpty`, defaults to disabled. - `OnPermissionRequest` (PermissionHandlerFunc): Optional handler called before each tool execution to approve or deny it. When nil, permission requests are emitted as events and left pending for manual resolution. `copilot.PermissionHandler.ApproveAll` approves requests when managed settings are disabled and returns an error when `EnableManagedSettings` is true. Custom handlers can inspect `RequiresManagedApproval()` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` (UserInputHandler): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. diff --git a/java/README.md b/java/README.md index 1ff1c90c7..b772a2ce8 100644 --- a/java/README.md +++ b/java/README.md @@ -127,6 +127,8 @@ and `setExcludedTools(...)`, prefer the source-qualified filter form `DefaultAgentConfig.setExcludedTools(...)`, use `-` directly. +`CopilotClientOptions.setCwd(...)` sets the runtime process working directory, which otherwise inherits the current process working directory. `SessionConfig.setWorkingDirectory(...)` sets the session working directory, which otherwise defaults to the runtime process working directory. + ## Permission Handling `PermissionHandler.APPROVE_ALL` approves requests when managed settings are disabled. When `enableManagedSettings` is true, it completes exceptionally. Custom handlers can inspect `request.getManagedApprovalRequired()` for human-facing confirmation logic. diff --git a/nodejs/README.md b/nodejs/README.md index 416d9bb0d..20886e79a 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -135,6 +135,7 @@ Create a new conversation session. - `tools?: Tool[]` - Custom tools exposed to the CLI. Tools without `handler` are declaration-only and must be resolved via pending tool-call RPCs. - `systemMessage?: SystemMessageConfig` - System message customization (see below) - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below) +- `workingDirectory?: string` - Working directory for the session (default: runtime process cwd). - `enableSessionStore?: boolean` - Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section. - `onPermissionRequest?: PermissionHandler` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `approveAll` approves requests when managed settings are disabled and throws when `enableManagedSettings` is true. Custom handlers can inspect `managedApprovalRequired` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. diff --git a/python/README.md b/python/README.md index 8fdb1f191..e540f9689 100644 --- a/python/README.md +++ b/python/README.md @@ -279,6 +279,7 @@ These are passed as keyword arguments to `create_session()`: - `streaming` (bool): Enable streaming delta events - `provider` (ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. - `infinite_sessions` (InfiniteSessionConfig): Automatic context compaction configuration +- `working_directory` (str | None): Working directory for the session (default: runtime process working directory). - `enable_session_store` (bool): Enables the cross-session store for search and retrieval across sessions. When unset in `"copilot-cli"` mode, the runtime default applies (enabled). In `"empty"` mode, defaults to disabled. - `on_permission_request` (callable): Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. `PermissionHandler.approve_all` approves requests when managed settings are disabled and raises an error when `enable_managed_settings` is true. Custom handlers can inspect `managed_approval_required` for human-facing confirmation logic. See [Permission Handling](#permission-handling) section. - `on_user_input_request` (callable): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. diff --git a/rust/README.md b/rust/README.md index eccc29aa2..705166cdd 100644 --- a/rust/README.md +++ b/rust/README.md @@ -108,6 +108,8 @@ With the default `CliProgram::Resolve`, `Client::start()` resolves the CLI in th Created via `Client::create_session` or `Client::resume_session`. Owns an internal event loop that dispatches CLI callbacks to the focused handler traits you install on `SessionConfig`, and broadcasts session events through `subscribe()`. +`SessionConfig::working_directory` sets the session working directory. When unset, the runtime uses its process working directory. + ```rust,ignore use github_copilot_sdk::MessageOptions; From 4405f45290a136e38081e979e264d8c2bd594021 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 4 Aug 2026 15:10:23 +0100 Subject: [PATCH 21/72] Speed up Rust E2E tests with shared clients (#2250) Reuse file-scoped Copilot clients for eligible Rust E2E tests while retaining dedicated clients for startup and lifecycle-sensitive cases. Keep shared router tasks on a persistent runtime and bound harness cleanup. --- rust/src/lib.rs | 54 + rust/tests/e2e/abort.rs | 109 +- rust/tests/e2e/ask_user.rs | 15 +- rust/tests/e2e/builtin_tools.rs | 21 +- rust/tests/e2e/canvas.rs | 163 +-- rust/tests/e2e/client_api.rs | 75 +- rust/tests/e2e/commands.rs | 16 +- rust/tests/e2e/compaction.rs | 13 +- rust/tests/e2e/elicitation.rs | 166 +-- rust/tests/e2e/event_fidelity.rs | 28 +- rust/tests/e2e/hooks.rs | 14 +- rust/tests/e2e/hooks_extended.rs | 147 ++- rust/tests/e2e/mode_empty.rs | 70 +- rust/tests/e2e/mode_handlers.rs | 12 +- rust/tests/e2e/multi_provider_registry.rs | 8 +- rust/tests/e2e/multi_turn.rs | 12 +- rust/tests/e2e/permissions.rs | 35 +- rust/tests/e2e/pre_mcp_tool_call_hook.rs | 10 +- rust/tests/e2e/rpc_additional_edge_cases.rs | 43 +- rust/tests/e2e/rpc_agent.rs | 293 +++-- rust/tests/e2e/rpc_event_log.rs | 16 +- rust/tests/e2e/rpc_event_side_effects.rs | 25 +- rust/tests/e2e/rpc_mcp_and_skills.rs | 171 +-- rust/tests/e2e/rpc_mcp_config.rs | 10 +- rust/tests/e2e/rpc_mcp_lifecycle.rs | 22 +- rust/tests/e2e/rpc_queue.rs | 10 +- rust/tests/e2e/rpc_remote.rs | 13 +- rust/tests/e2e/rpc_schedule.rs | 10 +- rust/tests/e2e/rpc_server.rs | 119 +- rust/tests/e2e/rpc_server_misc.rs | 45 +- rust/tests/e2e/rpc_server_plugins.rs | 25 +- rust/tests/e2e/rpc_server_remote_control.rs | 19 +- rust/tests/e2e/rpc_session_state.rs | 155 ++- rust/tests/e2e/rpc_session_state_extras.rs | 35 +- rust/tests/e2e/rpc_shell_and_fleet.rs | 78 +- rust/tests/e2e/rpc_shell_edge_cases.rs | 25 +- rust/tests/e2e/rpc_shell_user_requested.rs | 10 +- rust/tests/e2e/rpc_tasks_and_handlers.rs | 18 +- rust/tests/e2e/rpc_ui_ephemeral_query.rs | 7 +- rust/tests/e2e/rpc_workspace_checkpoints.rs | 26 +- rust/tests/e2e/session.rs | 1314 ++++++++++--------- rust/tests/e2e/session_config.rs | 19 +- rust/tests/e2e/session_fs_sqlite.rs | 33 +- rust/tests/e2e/session_lifecycle.rs | 18 +- rust/tests/e2e/session_todos_changed.rs | 7 +- rust/tests/e2e/skills.rs | 15 +- rust/tests/e2e/streaming_fidelity.rs | 20 +- rust/tests/e2e/support.rs | 545 +++++++- rust/tests/e2e/system_message_sections.rs | 10 +- rust/tests/e2e/tool_results.rs | 16 +- rust/tests/e2e/tools.rs | 155 ++- 51 files changed, 2652 insertions(+), 1643 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f998d7225..cafa3c596 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2196,6 +2196,60 @@ impl Client { Ok(()) } + /// Start this client's notification and request router on the current runtime. + /// This is test-harness plumbing, not part of the supported SDK API. + #[cfg(feature = "test-support")] + #[doc(hidden)] + pub fn start_router_for_test(&self) { + self.inner.router.ensure_started( + &self.inner.notification_tx, + &self.inner.request_rx, + self.inner.llm_inference.get().cloned(), + self.inner.on_github_telemetry.clone(), + ); + } + + #[cfg(feature = "test-support")] + #[doc(hidden)] + /// Disconnect and delete every session owned by this test client's isolated + /// runtime. This is test-harness plumbing, not part of the supported SDK API. + pub async fn cleanup_sessions_for_test(&self) -> Result<()> { + let mut first_error = None; + + for session_id in self.inner.router.session_ids() { + if let Err(error) = self + .call( + "session.destroy", + Some(serde_json::json!({ "sessionId": session_id })), + ) + .await + && first_error.is_none() + { + first_error = Some(error); + } + self.inner.router.unregister(&session_id); + } + + match self.list_sessions(None).await { + Ok(sessions) => { + for session in sessions { + if let Err(error) = self.delete_session(&session.session_id).await + && first_error.is_none() + { + first_error = Some(error); + } + } + } + Err(error) if first_error.is_none() => first_error = Some(error), + Err(_) => {} + } + + match first_error { + Some(error) => Err(error), + None => Ok(()), + } + } + /// Return the ID of the most recently updated session, if any. /// /// Useful for resuming the last conversation when the session ID was diff --git a/rust/tests/e2e/abort.rs b/rust/tests/e2e/abort.rs index d4e79452b..34fc66b60 100644 --- a/rust/tests/e2e/abort.rs +++ b/rust/tests/e2e/abort.rs @@ -10,22 +10,24 @@ use tokio::sync::{Mutex, mpsc, oneshot}; use super::support::{ DEFAULT_TEST_TOKEN, assistant_message_content, recv_with_timeout, wait_for_event, - with_e2e_context, }; #[tokio::test] async fn should_abort_during_active_streaming() { - with_e2e_context("abort", "should_abort_during_active_streaming", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_streaming(true)) - .await - .expect("create session"); - let events = session.subscribe(); + super::support::with_dedicated_e2e_context( + "abort", + "should_abort_during_active_streaming", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config().with_streaming(true)) + .await + .expect("create session"); + let events = session.subscribe(); - session + session .send( "Write a very long essay about the history of computing, covering every decade \ from the 1940s to the 2020s in great detail.", @@ -33,54 +35,55 @@ async fn should_abort_during_active_streaming() { .await .expect("send long streaming turn"); - let delta = wait_for_event(events, "assistant.message_delta", |event| { - event.parsed_type() == SessionEventType::AssistantMessageDelta + let delta = wait_for_event(events, "assistant.message_delta", |event| { + event.parsed_type() == SessionEventType::AssistantMessageDelta + }) + .await; + assert!( + !delta + .typed_data::() + .expect("assistant.message_delta data") + .delta_content + .is_empty() + ); + + session.abort().await.expect("abort session"); + + // Session should be usable after abort. Wait for the specific recovery + // message rather than racing against a late idle from the aborted turn. + let recovery_events = session.subscribe(); + session + .send("Say 'abort_recovery_ok'.") + .await + .expect("send recovery"); + let recovery = wait_for_event( + recovery_events, + "assistant.message containing abort_recovery_ok", + |event| { + event.parsed_type() == SessionEventType::AssistantMessage + && assistant_message_content(event) + .to_lowercase() + .contains("abort_recovery_ok") + }, + ) + .await; + assert!( + assistant_message_content(&recovery) + .to_lowercase() + .contains("abort_recovery_ok") + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); }) - .await; - assert!( - !delta - .typed_data::() - .expect("assistant.message_delta data") - .delta_content - .is_empty() - ); - - session.abort().await.expect("abort session"); - - // Session should be usable after abort. Wait for the specific recovery - // message rather than racing against a late idle from the aborted turn. - let recovery_events = session.subscribe(); - session - .send("Say 'abort_recovery_ok'.") - .await - .expect("send recovery"); - let recovery = wait_for_event( - recovery_events, - "assistant.message containing abort_recovery_ok", - |event| { - event.parsed_type() == SessionEventType::AssistantMessage - && assistant_message_content(event) - .to_lowercase() - .contains("abort_recovery_ok") - }, - ) - .await; - assert!( - assistant_message_content(&recovery) - .to_lowercase() - .contains("abort_recovery_ok") - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + }, + ) .await; } #[tokio::test] async fn should_abort_during_active_tool_execution() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "abort", "should_abort_during_active_tool_execution", |ctx| { diff --git a/rust/tests/e2e/ask_user.rs b/rust/tests/e2e/ask_user.rs index c134ad3c9..d7d089358 100644 --- a/rust/tests/e2e/ask_user.rs +++ b/rust/tests/e2e/ask_user.rs @@ -12,13 +12,11 @@ use github_copilot_sdk::{ use serde_json::json; use tokio::sync::{Notify, mpsc}; -use super::support::{ - DEFAULT_TEST_TOKEN, assistant_message_content, recv_with_timeout, with_e2e_context, -}; +use super::support::{DEFAULT_TEST_TOKEN, assistant_message_content, recv_with_timeout}; #[tokio::test] async fn should_invoke_user_input_handler_when_model_uses_ask_user_tool() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "ask_user", "should_invoke_user_input_handler_when_model_uses_ask_user_tool", |ctx| { @@ -62,7 +60,7 @@ async fn should_invoke_user_input_handler_when_model_uses_ask_user_tool() { #[tokio::test] async fn should_receive_choices_in_user_input_request() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "ask_user", "should_receive_choices_in_user_input_request", |ctx| { @@ -107,7 +105,7 @@ async fn should_receive_choices_in_user_input_request() { #[tokio::test] async fn should_handle_freeform_user_input_response() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "ask_user", "should_handle_freeform_user_input_response", |ctx| { @@ -164,7 +162,8 @@ async fn should_handle_freeform_user_input_response() { /// the handler observes the sibling tool while its own request is still pending. #[tokio::test] async fn ask_user_does_not_block_sibling_tool_call_in_same_turn() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "ask_user", "ask_user_does_not_block_sibling_tool_call_in_same_turn", |ctx| { @@ -346,3 +345,5 @@ impl ToolHandler for SetMarkerTool { Ok(ToolResult::Text(format!("MARKER_{}", value.to_uppercase()))) } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("ask_user", 4); diff --git a/rust/tests/e2e/builtin_tools.rs b/rust/tests/e2e/builtin_tools.rs index 41584d3a0..12bcad4fa 100644 --- a/rust/tests/e2e/builtin_tools.rs +++ b/rust/tests/e2e/builtin_tools.rs @@ -2,7 +2,7 @@ use std::time::Duration; use github_copilot_sdk::MessageOptions; -use super::support::{assistant_message_content, with_e2e_context}; +use super::support::assistant_message_content; /// Built-in tool tests spawn a real CLI subprocess and execute actual shell / /// file tools. Under concurrent Windows CI load (e2e runs 4-wide on a 4-vCPU @@ -16,7 +16,8 @@ fn message(prompt: &str) -> MessageOptions { #[tokio::test] async fn should_capture_exit_code_in_output() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "builtin_tools", "should_capture_exit_code_in_output", |ctx| { @@ -49,7 +50,7 @@ async fn should_capture_exit_code_in_output() { #[tokio::test] async fn should_capture_stderr_output() { - with_e2e_context("builtin_tools", "should_capture_stderr_output", |ctx| { + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_capture_stderr_output", |ctx| { Box::pin(async move { if cfg!(windows) { return; @@ -77,7 +78,7 @@ async fn should_capture_stderr_output() { #[tokio::test] async fn should_read_file_with_line_range() { - with_e2e_context("builtin_tools", "should_read_file_with_line_range", |ctx| { + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_read_file_with_line_range", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); std::fs::write(ctx.work_dir().join("lines.txt"), "line1\nline2\nline3\nline4\nline5\n") @@ -106,7 +107,7 @@ async fn should_read_file_with_line_range() { #[tokio::test] async fn should_handle_nonexistent_file_gracefully() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_handle_nonexistent_file_gracefully", |ctx| { @@ -144,7 +145,7 @@ async fn should_handle_nonexistent_file_gracefully() { #[tokio::test] async fn should_edit_a_file_successfully() { - with_e2e_context("builtin_tools", "should_edit_a_file_successfully", |ctx| { + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_edit_a_file_successfully", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); std::fs::write(ctx.work_dir().join("edit_me.txt"), "Hello World\nGoodbye World\n") @@ -171,7 +172,7 @@ async fn should_edit_a_file_successfully() { #[tokio::test] async fn should_create_a_new_file() { - with_e2e_context("builtin_tools", "should_create_a_new_file", |ctx| { + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_create_a_new_file", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -196,7 +197,7 @@ async fn should_create_a_new_file() { #[tokio::test] async fn should_search_for_patterns_in_files() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_search_for_patterns_in_files", |ctx| { @@ -229,7 +230,7 @@ async fn should_search_for_patterns_in_files() { #[tokio::test] async fn should_find_files_by_pattern() { - with_e2e_context("builtin_tools", "should_find_files_by_pattern", |ctx| { + super::support::with_shared_e2e_context(&E2E, "builtin_tools", "should_find_files_by_pattern", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let src = ctx.work_dir().join("src"); @@ -256,3 +257,5 @@ async fn should_find_files_by_pattern() { }) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("builtin_tools", 8); diff --git a/rust/tests/e2e/canvas.rs b/rust/tests/e2e/canvas.rs index 1736e9711..2418e9e5a 100644 --- a/rust/tests/e2e/canvas.rs +++ b/rust/tests/e2e/canvas.rs @@ -10,8 +10,6 @@ use github_copilot_sdk::types::ExtensionInfo; use parking_lot::Mutex; use serde_json::{Value, json}; -use super::support::with_e2e_context; - struct TestCanvasHandler { open_calls: Mutex>, close_calls: Mutex>, @@ -74,33 +72,38 @@ fn canvas_session_config( #[tokio::test] async fn canvas_list_discovers_declared_canvases() { - with_e2e_context("canvas", "canvas_list_discovers_declared_canvases", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let handler = Arc::new(TestCanvasHandler::new()); - let session = client - .create_session(canvas_session_config(ctx, handler)) - .await - .expect("create session"); - - let result = session.rpc().canvas().list().await.expect("list canvases"); - - assert_eq!(result.canvases.len(), 1); - assert_eq!(result.canvases[0].canvas_id, "counter"); - assert_eq!(result.canvases[0].display_name, "Counter"); - assert_eq!(result.canvases[0].description, "Tracks a counter value."); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "canvas", + "canvas_list_discovers_declared_canvases", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let handler = Arc::new(TestCanvasHandler::new()); + let session = client + .create_session(canvas_session_config(ctx, handler)) + .await + .expect("create session"); + + let result = session.rpc().canvas().list().await.expect("list canvases"); + + assert_eq!(result.canvases.len(), 1); + assert_eq!(result.canvases[0].canvas_id, "counter"); + assert_eq!(result.canvases[0].display_name, "Counter"); + assert_eq!(result.canvases[0].description, "Tracks a counter value."); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn canvas_open_round_trip() { - with_e2e_context("canvas", "canvas_open_round_trip", |ctx| { + super::support::with_shared_e2e_context(&E2E, "canvas", "canvas_open_round_trip", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -158,64 +161,69 @@ async fn canvas_open_round_trip() { #[tokio::test] async fn canvas_invoke_action_round_trip() { - with_e2e_context("canvas", "canvas_invoke_action_round_trip", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let handler = Arc::new(TestCanvasHandler::new()); - let session = client - .create_session(canvas_session_config(ctx, handler.clone())) - .await - .expect("create session"); - - let canvas_list = session.rpc().canvas().list().await.expect("list canvases"); - let canvas = &canvas_list.canvases[0]; - - session - .rpc() - .canvas() - .open(github_copilot_sdk::rpc::CanvasOpenRequest { - canvas_id: "counter".to_string(), - instance_id: "counter-2".to_string(), - extension_id: Some(canvas.extension_id.clone()), - input: Some(json!({})), - }) - .await - .expect("open canvas"); - - let result = session - .rpc() - .canvas() - .action() - .invoke(github_copilot_sdk::rpc::CanvasActionInvokeRequest { - instance_id: "counter-2".to_string(), - action_name: "increment".to_string(), - input: Some(json!({ "delta": 1 })), - }) - .await - .expect("invoke action"); - - assert_eq!(result.result, Some(json!({ "newValue": 42 }))); - - { - let actions = handler.action_calls.lock(); - assert_eq!(actions.len(), 1); - assert_eq!(actions[0].canvas_id, "counter"); - assert_eq!(actions[0].instance_id, "counter-2"); - assert_eq!(actions[0].action_name, "increment"); - assert_eq!(actions[0].input, Some(json!({ "delta": 1 }))); - } - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "canvas", + "canvas_invoke_action_round_trip", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let handler = Arc::new(TestCanvasHandler::new()); + let session = client + .create_session(canvas_session_config(ctx, handler.clone())) + .await + .expect("create session"); + + let canvas_list = session.rpc().canvas().list().await.expect("list canvases"); + let canvas = &canvas_list.canvases[0]; + + session + .rpc() + .canvas() + .open(github_copilot_sdk::rpc::CanvasOpenRequest { + canvas_id: "counter".to_string(), + instance_id: "counter-2".to_string(), + extension_id: Some(canvas.extension_id.clone()), + input: Some(json!({})), + }) + .await + .expect("open canvas"); + + let result = session + .rpc() + .canvas() + .action() + .invoke(github_copilot_sdk::rpc::CanvasActionInvokeRequest { + instance_id: "counter-2".to_string(), + action_name: "increment".to_string(), + input: Some(json!({ "delta": 1 })), + }) + .await + .expect("invoke action"); + + assert_eq!(result.result, Some(json!({ "newValue": 42 }))); + + { + let actions = handler.action_calls.lock(); + assert_eq!(actions.len(), 1); + assert_eq!(actions[0].canvas_id, "counter"); + assert_eq!(actions[0].instance_id, "counter-2"); + assert_eq!(actions[0].action_name, "increment"); + assert_eq!(actions[0].input, Some(json!({ "delta": 1 }))); + } + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn canvas_close_round_trip() { - with_e2e_context("canvas", "canvas_close_round_trip", |ctx| { + super::support::with_shared_e2e_context(&E2E, "canvas", "canvas_close_round_trip", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -272,3 +280,4 @@ async fn canvas_close_round_trip() { }) .await; } +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("canvas", 4); diff --git a/rust/tests/e2e/client_api.rs b/rust/tests/e2e/client_api.rs index 951fe8720..35cdf6f28 100644 --- a/rust/tests/e2e/client_api.rs +++ b/rust/tests/e2e/client_api.rs @@ -1,41 +1,47 @@ use github_copilot_sdk::SessionId; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn should_delete_session_by_id() { - with_e2e_context("client_api", "should_delete_session_by_id", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let session_id = session.id().clone(); - - session.send_and_wait("Say OK.").await.expect("send"); - session.disconnect().await.expect("disconnect session"); - client - .delete_session(&session_id) - .await - .expect("delete session"); - - let metadata = client - .get_session_metadata(&session_id) - .await - .expect("get metadata"); - assert!(metadata.is_none()); - - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "client_api", + "should_delete_session_by_id", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let session_id = session.id().clone(); + + session.send_and_wait("Say OK.").await.expect("send"); + session.disconnect().await.expect("disconnect session"); + client + .delete_session(&session_id) + .await + .expect("delete session"); + + let metadata = client + .get_session_metadata(&session_id) + .await + .expect("get metadata"); + assert!(metadata.is_none()); + + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_report_error_when_deleting_unknown_session_id() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "client_api", "should_report_error_when_deleting_unknown_session_id", |ctx| { @@ -62,7 +68,7 @@ async fn should_report_error_when_deleting_unknown_session_id() { #[tokio::test] async fn should_get_null_last_session_id_before_any_sessions_exist() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "client_api", "should_get_null_last_session_id_before_any_sessions_exist", |ctx| { @@ -81,7 +87,8 @@ async fn should_get_null_last_session_id_before_any_sessions_exist() { #[tokio::test] async fn should_track_last_session_id_after_session_created() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "client_api", "should_track_last_session_id_after_session_created", |ctx| { @@ -122,7 +129,8 @@ async fn should_track_last_session_id_after_session_created() { #[tokio::test] async fn should_get_null_foreground_session_id_in_headless_mode() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "client_api", "should_get_null_foreground_session_id_in_headless_mode", |ctx| { @@ -144,7 +152,8 @@ async fn should_get_null_foreground_session_id_in_headless_mode() { #[tokio::test] async fn should_report_error_when_setting_foreground_session_in_headless_mode() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "client_api", "should_report_error_when_setting_foreground_session_in_headless_mode", |ctx| { @@ -175,3 +184,5 @@ async fn should_report_error_when_setting_foreground_session_in_headless_mode() ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("client_api", 5); diff --git a/rust/tests/e2e/commands.rs b/rust/tests/e2e/commands.rs index d6cb6699f..d110d3b35 100644 --- a/rust/tests/e2e/commands.rs +++ b/rust/tests/e2e/commands.rs @@ -11,11 +11,12 @@ use github_copilot_sdk::{CommandContext, CommandDefinition, CommandHandler, Requ use serde_json::json; use tokio::sync::mpsc; -use super::support::{recv_with_timeout, wait_for_event, with_e2e_context}; +use super::support::{recv_with_timeout, wait_for_event}; #[tokio::test] async fn session_commands_list_returns_builtins_and_respects_client_command_filter() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "commands", "session_with_commands_creates_successfully", |ctx| { @@ -85,7 +86,8 @@ async fn session_commands_list_returns_builtins_and_respects_client_command_filt #[tokio::test] async fn session_commands_invoke_known_builtin_returns_expected_result() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "commands", "session_with_no_commands_creates_successfully", |ctx| { @@ -129,7 +131,8 @@ async fn session_commands_invoke_known_builtin_returns_expected_result() { #[tokio::test] async fn session_commands_execute_runs_registered_command_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "commands", "session_with_commands_creates_successfully", |ctx| { @@ -175,7 +178,8 @@ async fn session_commands_execute_runs_registered_command_handler() { #[tokio::test] async fn session_commands_enqueue_and_respond_to_queued_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "commands", "session_with_no_commands_creates_successfully", |ctx| { @@ -289,3 +293,5 @@ fn assert_command( assert_eq!(command.kind, kind); assert!(!command.description.trim().is_empty()); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("commands", 4); diff --git a/rust/tests/e2e/compaction.rs b/rust/tests/e2e/compaction.rs index b9854ef1d..d56687d5f 100644 --- a/rust/tests/e2e/compaction.rs +++ b/rust/tests/e2e/compaction.rs @@ -1,10 +1,9 @@ use github_copilot_sdk::rpc::{LogRequest, SessionLogLevel}; -use super::support::with_e2e_context; - #[tokio::test] async fn should_return_empty_handoff_summary_for_fresh_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "compaction", "should_return_empty_handoff_summary_for_fresh_session", |ctx| { @@ -34,7 +33,8 @@ async fn should_return_empty_handoff_summary_for_fresh_session() { #[tokio::test] async fn should_report_noop_when_cancelling_compaction_without_inflight_work() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "compaction", "should_report_noop_when_cancelling_compaction_without_inflight_work", |ctx| { @@ -71,7 +71,8 @@ async fn should_report_noop_when_cancelling_compaction_without_inflight_work() { #[tokio::test] async fn should_summarize_for_handoff_after_non_ephemeral_log_event() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "compaction", "should_summarize_for_handoff_after_non_ephemeral_log_event", |ctx| { @@ -111,3 +112,5 @@ async fn should_summarize_for_handoff_after_non_ephemeral_log_event() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("compaction", 3); diff --git a/rust/tests/e2e/elicitation.rs b/rust/tests/e2e/elicitation.rs index 5575e67f3..31da30adb 100644 --- a/rust/tests/e2e/elicitation.rs +++ b/rust/tests/e2e/elicitation.rs @@ -10,11 +10,12 @@ use github_copilot_sdk::{ use serde_json::json; use tokio::sync::Mutex; -use super::support::{DEFAULT_TEST_TOKEN, assert_uuid_like, with_e2e_context}; +use super::support::{DEFAULT_TEST_TOKEN, assert_uuid_like}; #[tokio::test] async fn defaults_capabilities_when_not_provided() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "defaults_capabilities_when_not_provided", |ctx| { @@ -39,7 +40,8 @@ async fn defaults_capabilities_when_not_provided() { #[tokio::test] async fn elicitation_throws_when_capability_is_missing() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "elicitation_throws_when_capability_is_missing", |ctx| { @@ -83,7 +85,8 @@ async fn elicitation_throws_when_capability_is_missing() { #[tokio::test] async fn sends_requestelicitation_when_handler_provided() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "sends_requestelicitation_when_handler_provided", |ctx| { @@ -115,7 +118,8 @@ async fn sends_requestelicitation_when_handler_provided() { #[tokio::test] async fn should_report_elicitation_capability_based_on_handler_presence() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "should_report_elicitation_capability_based_on_handler_presence", |ctx| { @@ -161,7 +165,8 @@ async fn should_report_elicitation_capability_based_on_handler_presence() { #[tokio::test] async fn session_without_elicitationhandler_creates_successfully() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "session_without_elicitationhandler_creates_successfully", |ctx| { @@ -185,7 +190,8 @@ async fn session_without_elicitationhandler_creates_successfully() { #[tokio::test] async fn confirm_returns_true_when_handler_accepts() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "confirm_returns_true_when_handler_accepts", |ctx| { @@ -215,7 +221,8 @@ async fn confirm_returns_true_when_handler_accepts() { #[tokio::test] async fn confirm_returns_false_when_handler_declines() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "confirm_returns_false_when_handler_declines", |ctx| { @@ -243,83 +250,94 @@ async fn confirm_returns_false_when_handler_declines() { #[tokio::test] async fn select_returns_selected_option() { - with_e2e_context("elicitation", "select_returns_selected_option", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(DEFAULT_TEST_TOKEN) - .pipe_handler(QueuedElicitationHandler::new([accept( - json!({ "selection": "beta" }), - )])), - ) - .await - .expect("create session"); - - assert_eq!( - session - .ui() - .select("Choose", &["alpha", "beta"]) + super::support::with_shared_e2e_context( + &E2E, + "elicitation", + "select_returns_selected_option", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + SessionConfig::default() + .with_github_token(DEFAULT_TEST_TOKEN) + .pipe_handler(QueuedElicitationHandler::new([accept( + json!({ "selection": "beta" }), + )])), + ) .await - .expect("select") - .as_deref(), - Some("beta") - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + .expect("create session"); + + assert_eq!( + session + .ui() + .select("Choose", &["alpha", "beta"]) + .await + .expect("select") + .as_deref(), + Some("beta") + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn input_returns_freeform_value() { - with_e2e_context("elicitation", "input_returns_freeform_value", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(DEFAULT_TEST_TOKEN) - .pipe_handler(QueuedElicitationHandler::new([accept( - json!({ "value": "typed value" }), - )])), - ) - .await - .expect("create session"); - let options = UiInputOptions { - title: Some("Value"), - description: Some("A value to test"), - min_length: Some(1), - max_length: Some(20), - default: Some("default"), - ..UiInputOptions::default() - }; - - assert_eq!( - session - .ui() - .input("Enter value", Some(&options)) + super::support::with_shared_e2e_context( + &E2E, + "elicitation", + "input_returns_freeform_value", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + SessionConfig::default() + .with_github_token(DEFAULT_TEST_TOKEN) + .pipe_handler(QueuedElicitationHandler::new([accept( + json!({ "value": "typed value" }), + )])), + ) .await - .expect("input") - .as_deref(), - Some("typed value") - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + .expect("create session"); + let options = UiInputOptions { + title: Some("Value"), + description: Some("A value to test"), + min_length: Some(1), + max_length: Some(20), + default: Some("default"), + ..UiInputOptions::default() + }; + + assert_eq!( + session + .ui() + .input("Enter value", Some(&options)) + .await + .expect("input") + .as_deref(), + Some("typed value") + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn elicitation_returns_all_action_shapes() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "elicitation", "elicitation_returns_all_action_shapes", |ctx| { @@ -606,3 +624,5 @@ fn cancel() -> ElicitationResult { content: None, } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("elicitation", 10); diff --git a/rust/tests/e2e/event_fidelity.rs b/rust/tests/e2e/event_fidelity.rs index 770ed5da1..7176a7e66 100644 --- a/rust/tests/e2e/event_fidelity.rs +++ b/rust/tests/e2e/event_fidelity.rs @@ -3,11 +3,12 @@ use github_copilot_sdk::session_events::{ ToolExecutionCompleteData, ToolExecutionStartData, UserMessageData, }; -use super::support::{collect_until_idle, event_types, with_e2e_context}; +use super::support::{collect_until_idle, event_types}; #[tokio::test] async fn should_include_valid_fields_on_all_events() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_include_valid_fields_on_all_events", |ctx| { @@ -54,7 +55,8 @@ async fn should_include_valid_fields_on_all_events() { #[tokio::test] async fn should_emit_tool_execution_events_with_correct_fields() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_tool_execution_events_with_correct_fields", |ctx| { @@ -99,7 +101,8 @@ async fn should_emit_tool_execution_events_with_correct_fields() { #[tokio::test] async fn should_emit_assistant_usage_event_after_model_call() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_assistant_usage_event_after_model_call", |ctx| { @@ -136,7 +139,8 @@ async fn should_emit_assistant_usage_event_after_model_call() { #[tokio::test] async fn should_emit_session_usage_info_event_after_model_call() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_session_usage_info_event_after_model_call", |ctx| { @@ -175,7 +179,8 @@ async fn should_emit_session_usage_info_event_after_model_call() { #[tokio::test] async fn should_emit_pending_messages_modified_event_when_message_queue_changes() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_pending_messages_modified_event_when_message_queue_changes", |ctx| { @@ -218,7 +223,8 @@ async fn should_emit_pending_messages_modified_event_when_message_queue_changes( #[tokio::test] async fn should_emit_events_in_correct_order_for_tool_using_conversation() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_events_in_correct_order_for_tool_using_conversation", |ctx| { @@ -265,7 +271,8 @@ async fn should_emit_events_in_correct_order_for_tool_using_conversation() { #[tokio::test] async fn should_emit_assistant_message_with_messageid() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_emit_assistant_message_with_messageid", |ctx| { @@ -299,7 +306,8 @@ async fn should_emit_assistant_message_with_messageid() { #[tokio::test] async fn should_preserve_message_order_in_getmessages_after_tool_use() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "event_fidelity", "should_preserve_message_order_in_getmessages_after_tool_use", |ctx| { @@ -366,3 +374,5 @@ async fn should_preserve_message_order_in_getmessages_after_tool_use() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("event_fidelity", 8); diff --git a/rust/tests/e2e/hooks.rs b/rust/tests/e2e/hooks.rs index b4a211d87..051019073 100644 --- a/rust/tests/e2e/hooks.rs +++ b/rust/tests/e2e/hooks.rs @@ -7,11 +7,12 @@ use github_copilot_sdk::hooks::{ }; use tokio::sync::mpsc; -use super::support::{recv_with_timeout, with_e2e_context}; +use super::support::recv_with_timeout; #[tokio::test] async fn should_invoke_pretooluse_hook_when_model_runs_a_tool() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks", "should_invoke_pretooluse_hook_when_model_runs_a_tool", |ctx| { @@ -51,7 +52,8 @@ async fn should_invoke_pretooluse_hook_when_model_runs_a_tool() { #[tokio::test] async fn should_invoke_posttooluse_hook_after_model_runs_a_tool() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks", "should_invoke_posttooluse_hook_after_model_runs_a_tool", |ctx| { @@ -92,7 +94,7 @@ async fn should_invoke_posttooluse_hook_after_model_runs_a_tool() { #[tokio::test] async fn should_invoke_both_pretooluse_and_posttooluse_hooks_for_single_tool_call() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "hooks", "should_invoke_both_pretooluse_and_posttooluse_hooks_for_single_tool_call", |ctx| { @@ -147,7 +149,8 @@ async fn should_invoke_both_pretooluse_and_posttooluse_hooks_for_single_tool_cal #[tokio::test] async fn should_deny_tool_execution_when_pretooluse_returns_deny() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks", "should_deny_tool_execution_when_pretooluse_returns_deny", |ctx| { @@ -226,3 +229,4 @@ impl SessionHooks for RecordingHooks { None } } +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("hooks", 4); diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index 4c61757e8..b18460642 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -14,11 +14,12 @@ use github_copilot_sdk::{Error, SessionConfig, Tool, ToolInvocation, ToolResult} use serde_json::json; use tokio::sync::mpsc; -use super::support::{assistant_message_content, recv_with_timeout, with_e2e_context}; +use super::support::{assistant_message_content, recv_with_timeout}; #[tokio::test] async fn should_invoke_onsessionstart_hook_on_new_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_onsessionstart_hook_on_new_session", |ctx| { @@ -50,7 +51,8 @@ async fn should_invoke_onsessionstart_hook_on_new_session() { #[tokio::test] async fn should_invoke_onuserpromptsubmitted_hook_when_sending_a_message() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_onuserpromptsubmitted_hook_when_sending_a_message", |ctx| { @@ -82,7 +84,8 @@ async fn should_invoke_onuserpromptsubmitted_hook_when_sending_a_message() { #[tokio::test] async fn should_invoke_onsessionend_hook_when_session_is_disconnected() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_onsessionend_hook_when_session_is_disconnected", |ctx| { @@ -113,7 +116,8 @@ async fn should_invoke_onsessionend_hook_when_session_is_disconnected() { #[tokio::test] async fn should_invoke_onerroroccurred_hook_when_error_occurs() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_onerroroccurred_hook_when_error_occurs", |ctx| { @@ -144,7 +148,8 @@ async fn should_invoke_onerroroccurred_hook_when_error_occurs() { #[tokio::test] async fn should_invoke_userpromptsubmitted_hook_and_modify_prompt() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_userpromptsubmitted_hook_and_modify_prompt", |ctx| { @@ -186,69 +191,80 @@ async fn should_invoke_userpromptsubmitted_hook_and_modify_prompt() { #[tokio::test] async fn should_invoke_sessionstart_hook() { - with_e2e_context("hooks_extended", "should_invoke_sessionstart_hook", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::session_start( - tx, - Some(SessionStartOutput { - additional_context: Some("Session start hook context.".to_string()), - ..SessionStartOutput::default() - }), - ), - ))) - .await - .expect("create session"); - - session.send_and_wait("Say hi").await.expect("send"); - let input = recv_with_timeout(&mut rx, "sessionStart hook").await; - assert_eq!(input.source, "new"); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "hooks_extended", + "should_invoke_sessionstart_hook", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let (tx, mut rx) = mpsc::unbounded_channel(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( + RecordingHooks::session_start( + tx, + Some(SessionStartOutput { + additional_context: Some("Session start hook context.".to_string()), + ..SessionStartOutput::default() + }), + ), + ))) + .await + .expect("create session"); + + session.send_and_wait("Say hi").await.expect("send"); + let input = recv_with_timeout(&mut rx, "sessionStart hook").await; + assert_eq!(input.source, "new"); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_invoke_sessionend_hook() { - with_e2e_context("hooks_extended", "should_invoke_sessionend_hook", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let (tx, mut rx) = mpsc::unbounded_channel(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( - RecordingHooks::session_end( - tx, - Some(SessionEndOutput { - session_summary: Some("session ended".to_string()), - ..SessionEndOutput::default() - }), - ), - ))) - .await - .expect("create session"); - - session.send_and_wait("Say bye").await.expect("send"); - session.disconnect().await.expect("disconnect session"); - let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; - assert!(input.timestamp > 0.0); - - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "hooks_extended", + "should_invoke_sessionend_hook", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let (tx, mut rx) = mpsc::unbounded_channel(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config().with_hooks(Arc::new( + RecordingHooks::session_end( + tx, + Some(SessionEndOutput { + session_summary: Some("session ended".to_string()), + ..SessionEndOutput::default() + }), + ), + ))) + .await + .expect("create session"); + + session.send_and_wait("Say bye").await.expect("send"); + session.disconnect().await.expect("disconnect session"); + let input = recv_with_timeout(&mut rx, "sessionEnd hook").await; + assert!(input.timestamp > 0.0); + + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_register_erroroccurred_hook() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_register_erroroccurred_hook", |ctx| { @@ -284,7 +300,8 @@ async fn should_register_erroroccurred_hook() { #[tokio::test] async fn should_invoke_agentstop_hook_and_apply_block_response() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_invoke_agentstop_hook_and_apply_block_response", |ctx| { @@ -326,7 +343,8 @@ async fn should_invoke_agentstop_hook_and_apply_block_response() { #[tokio::test] async fn should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput", |ctx| { @@ -369,7 +387,8 @@ async fn should_allow_pretooluse_to_return_modifiedargs_and_suppressoutput() { #[tokio::test] async fn should_allow_posttooluse_to_return_modifiedresult() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "hooks_extended", "should_allow_posttooluse_to_return_modifiedresult", |ctx| { @@ -415,7 +434,7 @@ async fn should_allow_posttooluse_to_return_modifiedresult() { #[tokio::test] #[ignore = "Fails with 1.0.64-0 runtime: built-in tools are not available when hooks restrict availableTools, so the failure path cannot be exercised. Follow up with runtime team."] async fn should_invoke_posttoolusefailure_hook_for_failed_tool_result() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "hooks_extended", "should_invoke_posttoolusefailure_hook_for_failed_tool_result", |ctx| { @@ -719,3 +738,5 @@ impl ToolHandler for EchoValueTool { )) } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("hooks_extended", 11); diff --git a/rust/tests/e2e/mode_empty.rs b/rust/tests/e2e/mode_empty.rs index af1e9267e..2a62d66cf 100644 --- a/rust/tests/e2e/mode_empty.rs +++ b/rust/tests/e2e/mode_empty.rs @@ -12,10 +12,22 @@ use std::sync::Arc; use github_copilot_sdk::handler::ApproveAllHandler; use github_copilot_sdk::types::SystemMessageConfig; -use github_copilot_sdk::{BUILTIN_TOOLS_ISOLATED, Client, ClientMode, SessionConfig, ToolSet}; +use github_copilot_sdk::{BUILTIN_TOOLS_ISOLATED, ClientMode, SessionConfig, ToolSet}; use serde_json::Value; -use super::support::{assistant_message_content, with_e2e_context}; +use super::support::assistant_message_content; + +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::new("mode_empty", empty_shared_client_options, 6); + +fn empty_shared_client_options( + context: &super::support::E2eContext, +) -> github_copilot_sdk::ClientOptions { + context + .client_options() + .with_mode(ClientMode::Empty) + .with_base_directory(context.work_dir().to_path_buf()) +} const SHELL_TOOL_NAME: &str = if cfg!(windows) { "powershell" } else { "bash" }; @@ -85,17 +97,14 @@ fn system_message_from_request(exchange: &Value) -> String { #[tokio::test] async fn empty_mode_isolated_set_shell_tool_is_not_exposed() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_isolated_set_shell_tool_is_not_exposed", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() @@ -135,17 +144,14 @@ async fn empty_mode_isolated_set_shell_tool_is_not_exposed() { #[tokio::test] async fn empty_mode_builtin_star_exposes_all_built_in_tools() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_builtin_star_exposes_all_built_in_tools", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() @@ -175,17 +181,14 @@ async fn empty_mode_builtin_star_exposes_all_built_in_tools() { #[tokio::test] async fn empty_mode_excluded_tools_subtracts_from_available_tools() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_excluded_tools_subtracts_from_available_tools", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() @@ -217,17 +220,14 @@ async fn empty_mode_excluded_tools_subtracts_from_available_tools() { #[tokio::test] async fn empty_mode_strips_environment_context_from_the_system_message_by_default() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_strips_environment_context_from_the_system_message_by_default", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() @@ -274,17 +274,14 @@ async fn empty_mode_strips_environment_context_from_the_system_message_by_defaul #[tokio::test] async fn empty_mode_system_message_replace_llm_follows_caller_content_verbatim() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_system_message_replace_llm_follows_caller_content_verbatim", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() @@ -320,17 +317,14 @@ async fn empty_mode_system_message_replace_llm_follows_caller_content_verbatim() #[tokio::test] async fn empty_mode_append_caller_instruction_takes_effect_and_env_context_stripped() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_empty", "empty_mode_append_caller_instruction_takes_effect_and_env_context_stripped", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); - let options = ctx - .client_options() - .with_mode(ClientMode::Empty) - .with_base_directory(ctx.work_dir().to_path_buf()); - let client = Client::start(options).await.expect("start client"); + let client = ctx.start_client().await; let session = client .create_session( SessionConfig::default() diff --git a/rust/tests/e2e/mode_handlers.rs b/rust/tests/e2e/mode_handlers.rs index b4089ca28..7ab6fe5bf 100644 --- a/rust/tests/e2e/mode_handlers.rs +++ b/rust/tests/e2e/mode_handlers.rs @@ -15,9 +15,7 @@ use github_copilot_sdk::session_events::{ use github_copilot_sdk::{ExitPlanModeData, SessionConfig, SessionId}; use tokio::sync::mpsc; -use super::support::{ - recv_with_timeout, wait_for_event, wait_for_event_allowing_rate_limit, with_e2e_context, -}; +use super::support::{recv_with_timeout, wait_for_event, wait_for_event_allowing_rate_limit}; const MODE_HANDLER_TOKEN: &str = "mode-handler-token"; const PLAN_SUMMARY: &str = "Greeting file implementation plan"; @@ -64,7 +62,8 @@ impl AutoModeSwitchHandler for AutoModeHandler { #[tokio::test] async fn should_invoke_exit_plan_mode_handler_when_model_uses_tool() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_handlers", "should_invoke_exit_plan_mode_handler_when_model_uses_tool", |ctx| { @@ -181,7 +180,8 @@ async fn should_invoke_exit_plan_mode_handler_when_model_uses_tool() { #[tokio::test] async fn should_invoke_auto_mode_switch_handler_when_rate_limited() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "mode_handlers", "should_invoke_auto_mode_switch_handler_when_rate_limited", |ctx| { @@ -288,3 +288,5 @@ async fn should_invoke_auto_mode_switch_handler_when_rate_limited() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("mode_handlers", 2); diff --git a/rust/tests/e2e/multi_provider_registry.rs b/rust/tests/e2e/multi_provider_registry.rs index 8c37deaa2..d07acd356 100644 --- a/rust/tests/e2e/multi_provider_registry.rs +++ b/rust/tests/e2e/multi_provider_registry.rs @@ -5,8 +5,6 @@ use github_copilot_sdk::{ }; use serde_json::Value; -use super::support::with_e2e_context; - const CATEGORY: &str = "multi_provider_registry"; fn headers(provider: &str) -> HashMap { @@ -17,7 +15,8 @@ fn headers(provider: &str) -> HashMap { #[tokio::test] async fn should_register_multiple_providers_with_custom_agents_bound_to_their_models() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, CATEGORY, "should_register_multiple_providers_with_custom_agents_bound_to_their_models", |ctx| { @@ -124,7 +123,7 @@ async fn assert_routing( expected_wire_model: &'static str, expected_provider_header: &'static str, ) { - with_e2e_context(CATEGORY, snapshot_name, move |ctx| { + super::support::with_shared_e2e_context(&E2E, CATEGORY, snapshot_name, move |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -241,3 +240,4 @@ async fn should_route_delta_turbo_turn_to_its_provider_and_wire_model() { ) .await; } +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard(CATEGORY, 4); diff --git a/rust/tests/e2e/multi_turn.rs b/rust/tests/e2e/multi_turn.rs index 8c3bc5cb9..e57fe2294 100644 --- a/rust/tests/e2e/multi_turn.rs +++ b/rust/tests/e2e/multi_turn.rs @@ -1,13 +1,12 @@ use github_copilot_sdk::SessionEvent; use github_copilot_sdk::session_events::SessionEventType; -use super::support::{ - assistant_message_content, collect_until_idle, event_types, with_e2e_context, -}; +use super::support::{assistant_message_content, collect_until_idle, event_types}; #[tokio::test] async fn should_use_tool_results_from_previous_turns() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "multi_turn", "should_use_tool_results_from_previous_turns", |ctx| { @@ -52,7 +51,8 @@ async fn should_use_tool_results_from_previous_turns() { #[tokio::test] async fn should_handle_file_creation_then_reading_across_turns() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "multi_turn", "should_handle_file_creation_then_reading_across_turns", |ctx| { @@ -154,3 +154,5 @@ fn index_of( .skip(start_index) .find_map(|(index, event)| (event.parsed_type() == event_type).then_some(index)) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("multi_turn", 2); diff --git a/rust/tests/e2e/permissions.rs b/rust/tests/e2e/permissions.rs index e97aeacb0..8f594841f 100644 --- a/rust/tests/e2e/permissions.rs +++ b/rust/tests/e2e/permissions.rs @@ -11,12 +11,13 @@ use tokio::sync::{mpsc, oneshot}; use super::support::{ DEFAULT_TEST_TOKEN, assistant_message_content, recv_with_timeout, wait_for_condition, - wait_for_event, with_e2e_context, + wait_for_event, }; #[tokio::test] async fn should_work_with_approve_all_permission_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_work_with_approve_all_permission_handler", |ctx| { @@ -68,7 +69,8 @@ async fn should_handle_concurrent_permission_requests_from_parallel_tools() { #[tokio::test] async fn should_deny_permission_when_handler_returns_denied() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_deny_permission_when_handler_returns_denied", |ctx| { @@ -120,7 +122,8 @@ async fn should_deny_permission_when_handler_returns_denied() { #[tokio::test] async fn should_deny_tool_operations_when_handler_explicitly_denies() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_deny_tool_operations_when_handler_explicitly_denies", |ctx| { @@ -159,7 +162,8 @@ async fn should_deny_tool_operations_when_handler_explicitly_denies() { #[tokio::test] async fn should_handle_async_permission_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_handle_async_permission_handler", |ctx| { @@ -195,7 +199,7 @@ async fn should_handle_async_permission_handler() { #[tokio::test] async fn should_resume_session_with_permission_handler() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "permissions", "should_resume_session_with_permission_handler", |ctx| { @@ -250,7 +254,7 @@ async fn should_resume_session_with_permission_handler() { #[tokio::test] async fn should_deny_tool_operations_when_handler_explicitly_denies_after_resume() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "permissions", "should_deny_tool_operations_when_handler_explicitly_denies_after_resume", |ctx| { @@ -310,7 +314,8 @@ async fn should_deny_tool_operations_when_handler_explicitly_denies_after_resume #[tokio::test] async fn should_receive_toolcallid_in_permission_requests() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_receive_toolcallid_in_permission_requests", |ctx| { @@ -350,7 +355,8 @@ async fn should_receive_toolcallid_in_permission_requests() { #[tokio::test] async fn should_deny_permission_with_noresult_kind() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_deny_permission_with_noresult_kind", |ctx| { @@ -385,7 +391,8 @@ async fn should_deny_permission_with_noresult_kind() { #[tokio::test] async fn should_short_circuit_permission_handler_when_set_approve_all_enabled() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_short_circuit_permission_handler_when_set_approve_all_enabled", |ctx| { @@ -454,7 +461,8 @@ async fn should_short_circuit_permission_handler_when_set_approve_all_enabled() #[tokio::test] async fn should_wait_for_slow_permission_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_wait_for_slow_permission_handler", |ctx| { @@ -520,7 +528,8 @@ async fn should_wait_for_slow_permission_handler() { #[tokio::test] async fn should_invoke_permission_handler_for_write_operations() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "permissions", "should_invoke_permission_handler_for_write_operations", |ctx| { @@ -720,3 +729,5 @@ impl PermissionHandler for SlowPermissionHandler { PermissionResult::approve_once() } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("permissions", 9); diff --git a/rust/tests/e2e/pre_mcp_tool_call_hook.rs b/rust/tests/e2e/pre_mcp_tool_call_hook.rs index fd05796fc..31e69d106 100644 --- a/rust/tests/e2e/pre_mcp_tool_call_hook.rs +++ b/rust/tests/e2e/pre_mcp_tool_call_hook.rs @@ -8,7 +8,7 @@ use github_copilot_sdk::{IndexMap, McpServerConfig, McpStdioServerConfig}; use serde_json::{Value, json}; use tokio::sync::mpsc; -use super::support::{assistant_message_content, recv_with_timeout, with_e2e_context}; +use super::support::{assistant_message_content, recv_with_timeout}; fn meta_echo_mcp_servers(repo_root: &std::path::Path) -> IndexMap { let harness_dir = repo_root.join("test").join("harness"); @@ -88,7 +88,7 @@ impl SessionHooks for RemoveMetaHooks { #[tokio::test] async fn should_set_meta_via_premcptoolcall_hook() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "pre_mcp_tool_call_hook", "should_set_meta_via_premcptoolcall_hook", |ctx| { @@ -138,7 +138,7 @@ async fn should_set_meta_via_premcptoolcall_hook() { #[tokio::test] async fn should_replace_meta_via_premcptoolcall_hook() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "pre_mcp_tool_call_hook", "should_replace_meta_via_premcptoolcall_hook", |ctx| { @@ -186,7 +186,7 @@ async fn should_replace_meta_via_premcptoolcall_hook() { #[tokio::test] async fn should_remove_meta_via_premcptoolcall_hook() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "pre_mcp_tool_call_hook", "should_remove_meta_via_premcptoolcall_hook", |ctx| { @@ -231,3 +231,5 @@ async fn should_remove_meta_via_premcptoolcall_hook() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("pre_mcp_tool_call_hook", 3); diff --git a/rust/tests/e2e/rpc_additional_edge_cases.rs b/rust/tests/e2e/rpc_additional_edge_cases.rs index 59891e94a..d7537f314 100644 --- a/rust/tests/e2e/rpc_additional_edge_cases.rs +++ b/rust/tests/e2e/rpc_additional_edge_cases.rs @@ -5,11 +5,12 @@ use github_copilot_sdk::rpc::{ }; use github_copilot_sdk::session_events::SessionMode; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn shell_exec_with_zero_timeout_does_not_kill_long_running_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "shell_exec_with_zero_timeout_does_not_kill_long_running_command", |ctx| { @@ -49,7 +50,8 @@ async fn shell_exec_with_zero_timeout_does_not_kill_long_running_command() { #[tokio::test] async fn workspaces_create_file_with_empty_content_round_trips() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "workspaces_create_file_with_empty_content_round_trips", |ctx| { @@ -98,7 +100,8 @@ async fn workspaces_create_file_with_empty_content_round_trips() { #[tokio::test] async fn workspaces_create_file_with_unicode_content_round_trips() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "workspaces_create_file_with_unicode_content_round_trips", |ctx| { @@ -141,7 +144,8 @@ async fn workspaces_create_file_with_unicode_content_round_trips() { #[tokio::test] async fn workspaces_create_file_with_large_content_round_trips() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "workspaces_create_file_with_large_content_round_trips", |ctx| { @@ -187,7 +191,8 @@ async fn workspaces_create_file_with_large_content_round_trips() { #[tokio::test] async fn plan_update_with_empty_content_then_read_returns_empty() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "plan_update_with_empty_content_then_read_returns_empty", |ctx| { @@ -220,7 +225,8 @@ async fn plan_update_with_empty_content_then_read_returns_empty() { #[tokio::test] async fn plan_delete_when_none_exists_is_idempotent() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "plan_delete_when_none_exists_is_idempotent", |ctx| { @@ -252,7 +258,8 @@ async fn plan_delete_when_none_exists_is_idempotent() { #[tokio::test] async fn mode_set_to_same_value_multiple_times_stays_stable() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "mode_set_to_same_value_multiple_times_stays_stable", |ctx| { @@ -289,7 +296,8 @@ async fn mode_set_to_same_value_multiple_times_stays_stable() { #[tokio::test] async fn name_set_with_unicode_round_trips() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "name_set_with_unicode_round_trips", |ctx| { @@ -323,7 +331,8 @@ async fn name_set_with_unicode_round_trips() { #[tokio::test] async fn usage_get_metrics_on_fresh_session_returns_zero_tokens() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "usage_get_metrics_on_fresh_session_returns_zero_tokens", |ctx| { @@ -351,7 +360,8 @@ async fn usage_get_metrics_on_fresh_session_returns_zero_tokens() { #[tokio::test] async fn permissions_reset_session_approvals_on_fresh_session_is_noop() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "permissions_reset_session_approvals_on_fresh_session_is_noop", |ctx| { @@ -381,7 +391,8 @@ async fn permissions_reset_session_approvals_on_fresh_session_is_noop() { #[tokio::test] async fn permissions_set_approve_all_toggle_round_trips() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "permissions_set_approve_all_toggle_round_trips", |ctx| { @@ -440,7 +451,8 @@ async fn permissions_set_approve_all_toggle_round_trips() { #[tokio::test] async fn workspaces_createfile_then_listfiles_returns_all_files() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "workspaces_createfile_then_listfiles_returns_all_files", |ctx| { @@ -492,7 +504,8 @@ async fn workspaces_createfile_then_listfiles_returns_all_files() { #[tokio::test] async fn workspaces_getworkspace_returns_stable_result_across_calls() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_additional_edge_cases", "workspaces_getworkspace_returns_stable_result_across_calls", |ctx| { @@ -545,3 +558,5 @@ fn delayed_marker_command(marker_path: &std::path::Path) -> String { marker_path.display() ) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_additional_edge_cases", 13); diff --git a/rust/tests/e2e/rpc_agent.rs b/rust/tests/e2e/rpc_agent.rs index e254460bc..24fbd3067 100644 --- a/rust/tests/e2e/rpc_agent.rs +++ b/rust/tests/e2e/rpc_agent.rs @@ -3,41 +3,47 @@ use github_copilot_sdk::rpc::{AgentInfo, AgentSelectRequest}; use github_copilot_sdk::session_events::SessionEventType; use serde_json::json; -use super::support::{wait_for_event, with_e2e_context}; +use super::support::wait_for_event; #[tokio::test] async fn should_list_available_custom_agents() { - with_e2e_context("rpc_agents", "should_list_available_custom_agents", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_custom_agents(create_custom_agents()), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "rpc_agents", + "should_list_available_custom_agents", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_custom_agents(create_custom_agents()), + ) + .await + .expect("create session"); - let result = session.rpc().agent().list().await.expect("agent list"); - assert_agent(&result.agents, "test-agent", "Test Agent", "A test agent"); - assert_agent( - &result.agents, - "another-agent", - "Another Agent", - "Another test agent", - ); + let result = session.rpc().agent().list().await.expect("agent list"); + assert_agent(&result.agents, "test-agent", "Test Agent", "A test agent"); + assert_agent( + &result.agents, + "another-agent", + "Another Agent", + "Another test agent", + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_return_null_when_no_agent_is_selected() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_agents", "should_return_null_when_no_agent_is_selected", |ctx| { @@ -71,47 +77,53 @@ async fn should_return_null_when_no_agent_is_selected() { #[tokio::test] async fn should_select_and_get_current_agent() { - with_e2e_context("rpc_agents", "should_select_and_get_current_agent", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_custom_agents([create_custom_agents().remove(0)]), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "rpc_agents", + "should_select_and_get_current_agent", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_custom_agents([create_custom_agents().remove(0)]), + ) + .await + .expect("create session"); - let selected = session - .rpc() - .agent() - .select(AgentSelectRequest { - name: "test-agent".to_string(), - }) - .await - .expect("select agent"); - assert_eq!(selected.agent.name, "test-agent"); - assert_eq!(selected.agent.display_name, "Test Agent"); + let selected = session + .rpc() + .agent() + .select(AgentSelectRequest { + name: "test-agent".to_string(), + }) + .await + .expect("select agent"); + assert_eq!(selected.agent.name, "test-agent"); + assert_eq!(selected.agent.display_name, "Test Agent"); - let current = session - .rpc() - .agent() - .get_current() - .await - .expect("get selected agent"); - assert_eq!(current.agent.name, "test-agent"); + let current = session + .rpc() + .agent() + .get_current() + .await + .expect("get selected agent"); + assert_eq!(current.agent.name, "test-agent"); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_emit_subagent_selected_and_deselected_events() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_agents", "should_emit_subagent_selected_and_deselected_events", |ctx| { @@ -185,51 +197,57 @@ async fn should_emit_subagent_selected_and_deselected_events() { #[tokio::test] async fn should_deselect_current_agent() { - with_e2e_context("rpc_agents", "should_deselect_current_agent", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_custom_agents([create_custom_agents().remove(0)]), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "rpc_agents", + "should_deselect_current_agent", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_custom_agents([create_custom_agents().remove(0)]), + ) + .await + .expect("create session"); - session - .rpc() - .agent() - .select(AgentSelectRequest { - name: "test-agent".to_string(), - }) - .await - .expect("select agent"); - session - .rpc() - .agent() - .deselect() - .await - .expect("deselect agent"); - let value = client - .call( - "session.agent.getCurrent", - Some(json!({ "sessionId": session.id() })), - ) - .await - .expect("get current agent"); - assert!(value.get("agent").is_some_and(serde_json::Value::is_null)); + session + .rpc() + .agent() + .select(AgentSelectRequest { + name: "test-agent".to_string(), + }) + .await + .expect("select agent"); + session + .rpc() + .agent() + .deselect() + .await + .expect("deselect agent"); + let value = client + .call( + "session.agent.getCurrent", + Some(json!({ "sessionId": session.id() })), + ) + .await + .expect("get current agent"); + assert!(value.get("agent").is_some_and(serde_json::Value::is_null)); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_return_empty_list_when_no_custom_agents_configured() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_agents", "should_return_empty_list_when_no_custom_agents_configured", |ctx| { @@ -254,46 +272,53 @@ async fn should_return_empty_list_when_no_custom_agents_configured() { #[tokio::test] async fn should_call_agent_reload() { - with_e2e_context("rpc_agents", "should_call_agent_reload", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let reload_agent = - CustomAgentConfig::new("reload-test-agent-rust", "You are a reload test agent.") - .with_display_name("Reload Test Agent") - .with_description("Used by the agent reload RPC test."); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_custom_agents([reload_agent.clone()]), + super::support::with_shared_e2e_context( + &E2E, + "rpc_agents", + "should_call_agent_reload", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let reload_agent = CustomAgentConfig::new( + "reload-test-agent-rust", + "You are a reload test agent.", ) - .await - .expect("create session"); - - assert_agent( - &session - .rpc() - .agent() - .list() + .with_display_name("Reload Test Agent") + .with_description("Used by the agent reload RPC test."); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_custom_agents([reload_agent.clone()]), + ) .await - .expect("list before") - .agents, - "reload-test-agent-rust", - "Reload Test Agent", - "Used by the agent reload RPC test.", - ); - let reloaded = session.rpc().agent().reload().await.expect("reload agents"); - let current = session.rpc().agent().list().await.expect("list after"); - assert_eq!( - agent_names(&reloaded.agents), - agent_names(¤t.agents), - "reload result should match current list" - ); + .expect("create session"); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + assert_agent( + &session + .rpc() + .agent() + .list() + .await + .expect("list before") + .agents, + "reload-test-agent-rust", + "Reload Test Agent", + "Used by the agent reload RPC test.", + ); + let reloaded = session.rpc().agent().reload().await.expect("reload agents"); + let current = session.rpc().agent().list().await.expect("list after"); + assert_eq!( + agent_names(&reloaded.agents), + agent_names(¤t.agents), + "reload result should match current list" + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } @@ -322,3 +347,5 @@ fn agent_names(agents: &[AgentInfo]) -> Vec<&str> { names.sort_unstable(); names } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_agents", 7); diff --git a/rust/tests/e2e/rpc_event_log.rs b/rust/tests/e2e/rpc_event_log.rs index 84d575ee3..b116f3e50 100644 --- a/rust/tests/e2e/rpc_event_log.rs +++ b/rust/tests/e2e/rpc_event_log.rs @@ -7,11 +7,10 @@ use github_copilot_sdk::session_events::{ }; use serde_json::json; -use super::support::with_e2e_context; - #[tokio::test] async fn should_read_persisted_events_from_beginning() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_log", "should_read_persisted_events_from_beginning", |ctx| { @@ -73,7 +72,8 @@ async fn should_read_persisted_events_from_beginning() { #[tokio::test] async fn should_return_tail_cursor_and_read_empty_when_no_new_events() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_log", "should_return_tail_cursor_and_read_empty_when_no_new_events", |ctx| { @@ -116,7 +116,8 @@ async fn should_return_tail_cursor_and_read_empty_when_no_new_events() { #[tokio::test] async fn should_register_and_release_event_interest_idempotently() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_log", "should_register_and_release_event_interest_idempotently", |ctx| { @@ -162,7 +163,8 @@ async fn should_register_and_release_event_interest_idempotently() { #[tokio::test] async fn should_longpoll_with_types_filter_for_titlechanged_event() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_log", "should_longpoll_with_types_filter_for_titlechanged_event", |ctx| { @@ -213,3 +215,5 @@ async fn should_longpoll_with_types_filter_for_titlechanged_event() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_event_log", 4); diff --git a/rust/tests/e2e/rpc_event_side_effects.rs b/rust/tests/e2e/rpc_event_side_effects.rs index 4b634cb89..e8d7b29b2 100644 --- a/rust/tests/e2e/rpc_event_side_effects.rs +++ b/rust/tests/e2e/rpc_event_side_effects.rs @@ -8,11 +8,12 @@ use github_copilot_sdk::session_events::{ SessionWorkspaceFileChangedData, }; -use super::support::{assistant_message_content, wait_for_event, with_e2e_context}; +use super::support::{assistant_message_content, wait_for_event}; #[tokio::test] async fn should_emit_mode_changed_event_when_mode_set() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_mode_changed_event_when_mode_set", |ctx| { @@ -54,7 +55,8 @@ async fn should_emit_mode_changed_event_when_mode_set() { #[tokio::test] async fn should_emit_plan_changed_event_for_update_and_delete() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_plan_changed_event_for_update_and_delete", |ctx| { @@ -91,7 +93,8 @@ async fn should_emit_plan_changed_event_for_update_and_delete() { #[tokio::test] async fn should_emit_plan_changed_update_operation_on_second_update() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_plan_changed_update_operation_on_second_update", |ctx| { @@ -132,7 +135,8 @@ async fn should_emit_plan_changed_update_operation_on_second_update() { #[tokio::test] async fn should_emit_workspace_file_changed_event_when_file_created() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_workspace_file_changed_event_when_file_created", |ctx| { @@ -177,7 +181,8 @@ async fn should_emit_workspace_file_changed_event_when_file_created() { #[tokio::test] async fn should_emit_title_changed_event_when_name_set() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_title_changed_event_when_name_set", |ctx| { @@ -220,7 +225,8 @@ async fn should_emit_title_changed_event_when_name_set() { #[tokio::test] async fn should_emit_snapshot_rewind_event_and_remove_events_on_truncate() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_emit_snapshot_rewind_event_and_remove_events_on_truncate", |ctx| { @@ -281,7 +287,8 @@ async fn should_emit_snapshot_rewind_event_and_remove_events_on_truncate() { #[tokio::test] async fn should_allow_session_use_after_truncate() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_event_side_effects", "should_allow_session_use_after_truncate", |ctx| { @@ -351,3 +358,5 @@ fn wait_for_plan_event( == operation }) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_event_side_effects", 7); diff --git a/rust/tests/e2e/rpc_mcp_and_skills.rs b/rust/tests/e2e/rpc_mcp_and_skills.rs index eb8368ebc..d5a295e07 100644 --- a/rust/tests/e2e/rpc_mcp_and_skills.rs +++ b/rust/tests/e2e/rpc_mcp_and_skills.rs @@ -14,11 +14,10 @@ use github_copilot_sdk::rpc::{ }; use github_copilot_sdk::{IndexMap, McpServerConfig, McpStdioServerConfig}; -use super::support::with_e2e_context; - #[tokio::test] async fn should_list_and_toggle_session_skills() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_list_and_toggle_session_skills", |ctx| { @@ -87,7 +86,8 @@ async fn should_list_and_toggle_session_skills() { #[tokio::test] async fn should_ensure_skills_are_loaded_and_list_invoked_skills() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_ensure_skills_are_loaded_and_list_invoked_skills", |ctx| { @@ -137,7 +137,8 @@ async fn should_ensure_skills_are_loaded_and_list_invoked_skills() { #[tokio::test] async fn should_reload_session_skills() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_reload_session_skills", |ctx| { @@ -183,7 +184,8 @@ async fn should_reload_session_skills() { #[tokio::test] async fn should_list_mcp_servers_with_configured_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_list_mcp_servers_with_configured_server", |ctx| { @@ -217,7 +219,8 @@ async fn should_list_mcp_servers_with_configured_server() { #[tokio::test] async fn should_set_mcp_env_value_mode_and_remove_github_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_set_mcp_env_value_mode_and_remove_github_server", |ctx| { @@ -256,7 +259,8 @@ async fn should_set_mcp_env_value_mode_and_remove_github_server() { #[tokio::test] async fn should_report_mcp_sampling_failure_and_cancel_missing_sampling() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_mcp_sampling_failure_and_cancel_missing_sampling", |ctx| { @@ -312,76 +316,87 @@ async fn should_report_mcp_sampling_failure_and_cancel_missing_sampling() { #[tokio::test] async fn should_list_plugins() { - with_e2e_context("rpc_mcp_and_skills", "should_list_plugins", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - - let result = session.rpc().plugins().list().await.expect("plugins list"); - assert!( - result.plugins.iter().all(|plugin| !plugin.name.is_empty()), - "plugins should have names: {:?}", - result.plugins - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "rpc_mcp_and_skills", + "should_list_plugins", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + + let result = session.rpc().plugins().list().await.expect("plugins list"); + assert!( + result.plugins.iter().all(|plugin| !plugin.name.is_empty()), + "plugins should have names: {:?}", + result.plugins + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_list_extensions() { - with_e2e_context("rpc_mcp_and_skills", "should_list_extensions", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - session - .rpc() - .permissions() - .set_allow_all(PermissionsSetAllowAllRequest { - enabled: None, - mode: Some(PermissionsAllowAllMode::On), - model: None, - source: None, - }) - .await - .expect("enable allow-all"); - - let result = session - .rpc() - .extensions() - .list() - .await - .expect("extensions list"); - assert!( - result - .extensions - .iter() - .all(|extension| !extension.id.is_empty() && !extension.name.is_empty()), - "extensions should have ids and names: {:?}", - result.extensions - ); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "rpc_mcp_and_skills", + "should_list_extensions", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + session + .rpc() + .permissions() + .set_allow_all(PermissionsSetAllowAllRequest { + enabled: None, + mode: Some(PermissionsAllowAllMode::On), + model: None, + source: None, + }) + .await + .expect("enable allow-all"); + + let result = session + .rpc() + .extensions() + .list() + .await + .expect("extensions list"); + assert!( + result + .extensions + .iter() + .all(|extension| !extension.id.is_empty() && !extension.name.is_empty()), + "extensions should have ids and names: {:?}", + result.extensions + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_round_trip_mcp_app_host_context() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_round_trip_mcp_app_host_context", |ctx| { @@ -439,7 +454,8 @@ async fn should_round_trip_mcp_app_host_context() { #[tokio::test] async fn should_diagnose_and_report_mcp_app_capability_errors() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_diagnose_and_report_mcp_app_capability_errors", |ctx| { @@ -503,7 +519,8 @@ async fn should_diagnose_and_report_mcp_app_capability_errors() { #[tokio::test] async fn should_report_error_when_mcp_app_resource_is_not_available() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_error_when_mcp_app_resource_is_not_available", |ctx| { @@ -544,7 +561,8 @@ async fn should_report_error_when_mcp_app_resource_is_not_available() { #[tokio::test] async fn should_report_error_when_mcp_host_is_not_initialized() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_error_when_mcp_host_is_not_initialized", |ctx| { @@ -600,7 +618,8 @@ async fn should_report_error_when_mcp_host_is_not_initialized() { #[tokio::test] async fn should_report_error_when_mcp_oauth_server_is_not_configured() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_error_when_mcp_oauth_server_is_not_configured", |ctx| { @@ -639,7 +658,8 @@ async fn should_report_error_when_mcp_oauth_server_is_not_configured() { #[tokio::test] async fn should_report_error_when_mcp_oauth_server_is_not_remote() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_error_when_mcp_oauth_server_is_not_remote", |ctx| { @@ -680,7 +700,8 @@ async fn should_report_error_when_mcp_oauth_server_is_not_remote() { #[tokio::test] async fn should_report_error_when_extensions_are_not_available() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_and_skills", "should_report_error_when_extensions_are_not_available", |ctx| { @@ -814,3 +835,5 @@ async fn expect_err_contains( "expected error to contain {expected:?}, got {err}" ); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_mcp_and_skills", 15); diff --git a/rust/tests/e2e/rpc_mcp_config.rs b/rust/tests/e2e/rpc_mcp_config.rs index 506987fa1..591d7d247 100644 --- a/rust/tests/e2e/rpc_mcp_config.rs +++ b/rust/tests/e2e/rpc_mcp_config.rs @@ -4,11 +4,10 @@ use github_copilot_sdk::rpc::{ }; use serde_json::json; -use super::support::with_e2e_context; - #[tokio::test] async fn should_call_server_mcp_config_rpcs() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_config", "should_call_server_mcp_config_rpcs", |ctx| { @@ -91,7 +90,8 @@ async fn should_call_server_mcp_config_rpcs() { #[tokio::test] async fn should_round_trip_http_mcp_oauth_config_rpc() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_config", "should_round_trip_http_mcp_oauth_config_rpc", |ctx| { @@ -209,3 +209,5 @@ async fn should_round_trip_http_mcp_oauth_config_rpc() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_mcp_config", 2); diff --git a/rust/tests/e2e/rpc_mcp_lifecycle.rs b/rust/tests/e2e/rpc_mcp_lifecycle.rs index aa3adcf5c..9e135f1e9 100644 --- a/rust/tests/e2e/rpc_mcp_lifecycle.rs +++ b/rust/tests/e2e/rpc_mcp_lifecycle.rs @@ -10,11 +10,12 @@ use github_copilot_sdk::{Error, IndexMap, McpServerConfig, McpStdioServerConfig} use serde::de::DeserializeOwned; use serde_json::{Value, json}; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn should_list_tools_and_report_running_status_for_connected_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_list_tools_and_report_running_status_for_connected_server", |ctx| { @@ -61,7 +62,8 @@ async fn should_list_tools_and_report_running_status_for_connected_server() { #[tokio::test] async fn should_throw_when_listing_tools_for_unconnected_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_throw_when_listing_tools_for_unconnected_server", |ctx| { @@ -98,7 +100,8 @@ async fn should_throw_when_listing_tools_for_unconnected_server() { #[tokio::test] async fn should_stop_running_mcp_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_stop_running_mcp_server", |ctx| { @@ -137,7 +140,8 @@ async fn should_stop_running_mcp_server() { #[tokio::test] async fn should_start_and_restart_mcp_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_start_and_restart_mcp_server", |ctx| { @@ -202,7 +206,8 @@ async fn should_start_and_restart_mcp_server() { #[tokio::test] async fn should_reload_mcp_servers_with_config() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_reload_mcp_servers_with_config", |ctx| { @@ -244,7 +249,8 @@ async fn should_reload_mcp_servers_with_config() { #[tokio::test] async fn should_configure_github_mcp_server() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_mcp_lifecycle", "should_configure_github_mcp_server", |ctx| { @@ -374,3 +380,5 @@ fn assert_error_contains(err: &Error, expected: &str) { "expected error to contain {expected:?}, got {message}" ); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_mcp_lifecycle", 6); diff --git a/rust/tests/e2e/rpc_queue.rs b/rust/tests/e2e/rpc_queue.rs index 2c51f9e37..6f4f88165 100644 --- a/rust/tests/e2e/rpc_queue.rs +++ b/rust/tests/e2e/rpc_queue.rs @@ -7,7 +7,7 @@ use github_copilot_sdk::session_events::{CommandQueuedData, SessionEventType}; use serde_json::json; use uuid::Uuid; -use super::support::{wait_for_condition, wait_for_event, with_e2e_context}; +use super::support::{wait_for_condition, wait_for_event}; fn is_pending_command(item: &QueuePendingItems, command: &str) -> bool { item.kind == QueuePendingItemsKind::Command @@ -66,7 +66,8 @@ async fn wait_for_queue_empty(session: &Session) { #[tokio::test] async fn fresh_queue_is_empty_and_empty_mutations_are_noops() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_queue", "fresh_queue_is_empty_and_empty_mutations_are_noops", |ctx| { @@ -115,7 +116,8 @@ async fn fresh_queue_is_empty_and_empty_mutations_are_noops() { #[tokio::test] async fn pendingitems_reports_queued_command_and_remove_and_clear_update_queue() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_queue", "pendingitems_reports_queued_command_and_remove_and_clear_update_queue", |ctx| { @@ -223,3 +225,5 @@ async fn pendingitems_reports_queued_command_and_remove_and_clear_update_queue() ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_queue", 2); diff --git a/rust/tests/e2e/rpc_remote.rs b/rust/tests/e2e/rpc_remote.rs index c34a8d5e5..e98f6c4fa 100644 --- a/rust/tests/e2e/rpc_remote.rs +++ b/rust/tests/e2e/rpc_remote.rs @@ -1,11 +1,12 @@ use github_copilot_sdk::rpc::{RemoteEnableRequest, RemoteSessionMode}; use github_copilot_sdk::session_events::{SessionEventType, SessionRemoteSteerableChangedData}; -use super::support::{wait_for_event, with_e2e_context}; +use super::support::wait_for_event; #[tokio::test] async fn should_treat_remote_off_as_noop_or_implemented_error() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_remote", "should_treat_remote_off_as_noop_or_implemented_error", |ctx| { @@ -45,7 +46,8 @@ async fn should_treat_remote_off_as_noop_or_implemented_error() { #[tokio::test] async fn should_treat_remote_disable_as_noop_or_implemented_error() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_remote", "should_treat_remote_disable_as_noop_or_implemented_error", |ctx| { @@ -74,7 +76,8 @@ async fn should_treat_remote_disable_as_noop_or_implemented_error() { #[tokio::test] async fn should_notify_steerable_changed_event_and_persist_flag() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_remote", "should_notify_steerable_changed_event_and_persist_flag", |ctx| { @@ -112,3 +115,5 @@ async fn should_notify_steerable_changed_event_and_persist_flag() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_remote", 3); diff --git a/rust/tests/e2e/rpc_schedule.rs b/rust/tests/e2e/rpc_schedule.rs index fc782fe41..af8f6f59b 100644 --- a/rust/tests/e2e/rpc_schedule.rs +++ b/rust/tests/e2e/rpc_schedule.rs @@ -1,10 +1,9 @@ use github_copilot_sdk::rpc::ScheduleStopRequest; -use super::support::with_e2e_context; - #[tokio::test] async fn should_list_no_schedules_for_fresh_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_schedule", "should_list_no_schedules_for_fresh_session", |ctx| { @@ -34,7 +33,8 @@ async fn should_list_no_schedules_for_fresh_session() { #[tokio::test] async fn should_return_null_entry_when_stopping_unknown_schedule() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_schedule", "should_return_null_entry_when_stopping_unknown_schedule", |ctx| { @@ -71,3 +71,5 @@ async fn should_return_null_entry_when_stopping_unknown_schedule() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_schedule", 2); diff --git a/rust/tests/e2e/rpc_server.rs b/rust/tests/e2e/rpc_server.rs index d0beab245..caa846ba0 100644 --- a/rust/tests/e2e/rpc_server.rs +++ b/rust/tests/e2e/rpc_server.rs @@ -21,7 +21,8 @@ use super::support::{with_e2e_context, with_e2e_context_no_snapshot}; #[tokio::test] async fn should_call_rpc_ping_with_typed_params_and_result() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_call_rpc_ping_with_typed_params_and_result", |ctx| { @@ -118,7 +119,8 @@ async fn should_call_rpc_account_get_quota_when_authenticated() { #[tokio::test] async fn should_call_rpc_tools_list_with_typed_result() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_call_rpc_tools_list_with_typed_result", |ctx| { @@ -186,7 +188,8 @@ async fn should_reject_llm_response_frames_for_unknown_request() { #[tokio::test] async fn should_discover_server_mcp_and_skills() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_discover_server_mcp_and_skills", |ctx| { @@ -401,35 +404,41 @@ async fn should_call_rpc_sessionfs_setprovider_with_typed_result() { #[tokio::test] async fn should_add_secret_filter_values() { - with_e2e_context("rpc_server", "should_add_secret_filter_values", |ctx| { - Box::pin(async move { - let client = ctx.start_client().await; + super::support::with_shared_e2e_context( + &E2E, + "rpc_server", + "should_add_secret_filter_values", + |ctx| { + Box::pin(async move { + let client = ctx.start_client().await; - let result = client - .rpc() - .secrets() - .add_filter_values(SecretsAddFilterValuesRequest { - values: vec!["rust-secret-value".to_string()], - }) - .await; - match result { - Ok(result) => assert!(result.ok), - Err(err) => { - let message = err.to_string(); - assert!(message.contains("COPILOT_ENABLE_SECRET_FILTERING")); - assert!(!message.contains("Unhandled method secrets.addFilterValues")); + let result = client + .rpc() + .secrets() + .add_filter_values(SecretsAddFilterValuesRequest { + values: vec!["rust-secret-value".to_string()], + }) + .await; + match result { + Ok(response) => assert!(response.ok), + Err(err) => { + let message = err.to_string(); + assert!(message.contains("COPILOT_ENABLE_SECRET_FILTERING")); + assert!(!message.contains("Unhandled method secrets.addFilterValues")); + } } - } - client.stop().await.expect("stop client"); - }) - }) + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_list_find_and_inspect_persisted_session_state() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_list_find_and_inspect_persisted_session_state", |ctx| { @@ -550,7 +559,8 @@ async fn should_list_find_and_inspect_persisted_session_state() { #[tokio::test] async fn should_enrich_basic_session_metadata() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_enrich_basic_session_metadata", |ctx| { @@ -604,7 +614,8 @@ async fn should_enrich_basic_session_metadata() { #[tokio::test] async fn should_close_active_session_and_release_lock() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_close_active_session_and_release_lock", |ctx| { @@ -655,7 +666,8 @@ async fn should_close_active_session_and_release_lock() { #[tokio::test] async fn should_prune_dryrun_and_bulkdelete_persisted_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_prune_dryrun_and_bulkdelete_persisted_session", |ctx| { @@ -702,7 +714,8 @@ async fn should_prune_dryrun_and_bulkdelete_persisted_session() { #[tokio::test] async fn should_set_additional_plugins_and_reload_deferred_hooks() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_set_additional_plugins_and_reload_deferred_hooks", |ctx| { @@ -752,34 +765,40 @@ async fn should_set_additional_plugins_and_reload_deferred_hooks() { #[tokio::test] async fn should_save_and_get_event_file_path() { - with_e2e_context("rpc_server", "should_save_and_get_event_file_path", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "rpc_server", + "should_save_and_get_event_file_path", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - client - .rpc() - .sessions() - .save(SessionsSaveRequest { - session_id: session.id().clone(), - }) - .await - .expect("save session"); + client + .rpc() + .sessions() + .save(SessionsSaveRequest { + session_id: session.id().clone(), + }) + .await + .expect("save session"); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_report_implemented_error_when_connecting_unknown_remote_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server", "should_report_implemented_error_when_connecting_unknown_remote_session", |ctx| { @@ -861,3 +880,5 @@ fn paths_equal(left: &str, right: &str) -> bool { normalize(left) == normalize(right) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_server", 11); diff --git a/rust/tests/e2e/rpc_server_misc.rs b/rust/tests/e2e/rpc_server_misc.rs index b9e5cdf5c..47ae4ecbd 100644 --- a/rust/tests/e2e/rpc_server_misc.rs +++ b/rust/tests/e2e/rpc_server_misc.rs @@ -9,27 +9,33 @@ use super::support::{wait_for_condition, with_e2e_context}; #[tokio::test] async fn should_reload_user_settings() { - with_e2e_context("rpc_server_misc", "should_reload_user_settings", |ctx| { - Box::pin(async move { - let client = ctx.start_client().await; + super::support::with_shared_e2e_context( + &E2E, + "rpc_server_misc", + "should_reload_user_settings", + |ctx| { + Box::pin(async move { + let client = ctx.start_client().await; - client - .rpc() - .user() - .settings() - .reload() - .await - .expect("reload user settings"); + client + .rpc() + .user() + .settings() + .reload() + .await + .expect("reload user settings"); - client.stop().await.expect("stop client"); - }) - }) + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_get_set_and_clear_user_settings() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_misc", "should_get_set_and_clear_user_settings", |ctx| { @@ -206,7 +212,8 @@ async fn should_login_list_getcurrentauth_and_logout_account() { #[tokio::test] async fn should_report_agent_registry_spawn_gate_closed() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_misc", "should_report_agent_registry_spawn_gate_closed", |ctx| { @@ -279,7 +286,8 @@ async fn should_shut_down_owned_runtime() { #[tokio::test] async fn should_report_not_found_when_opening_session_without_context() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_misc", "should_report_not_found_when_opening_session_without_context", |ctx| { @@ -305,7 +313,8 @@ async fn should_report_not_found_when_opening_session_without_context() { #[tokio::test] async fn should_reject_send_attachments_from_non_extension_connection() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_misc", "should_reject_send_attachments_from_non_extension_connection", |ctx| { @@ -353,3 +362,5 @@ fn setting_patch(key: &str, value: Value) -> Value { settings.insert(key.to_string(), value); Value::Object(settings) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_server_misc", 5); diff --git a/rust/tests/e2e/rpc_server_plugins.rs b/rust/tests/e2e/rpc_server_plugins.rs index 054ffa359..df6072253 100644 --- a/rust/tests/e2e/rpc_server_plugins.rs +++ b/rust/tests/e2e/rpc_server_plugins.rs @@ -8,15 +8,14 @@ use github_copilot_sdk::rpc::{ PluginsUpdateRequest, }; -use super::support::with_e2e_context; - const MARKETPLACE_NAME: &str = "csharp-e2e-marketplace"; const PLUGIN_NAME: &str = "csharp-e2e-plugin"; const DIRECT_PLUGIN_NAME: &str = "csharp-e2e-direct"; #[tokio::test] async fn should_install_and_list_plugin_from_local_marketplace() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_install_and_list_plugin_from_local_marketplace", |ctx| { @@ -65,7 +64,8 @@ async fn should_install_and_list_plugin_from_local_marketplace() { #[tokio::test] async fn should_enable_and_disable_marketplace_plugin() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_enable_and_disable_marketplace_plugin", |ctx| { @@ -135,7 +135,8 @@ async fn should_enable_and_disable_marketplace_plugin() { #[tokio::test] async fn should_update_single_marketplace_plugin() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_update_single_marketplace_plugin", |ctx| { @@ -184,7 +185,8 @@ async fn should_update_single_marketplace_plugin() { #[tokio::test] async fn should_update_all_installed_plugins() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_update_all_installed_plugins", |ctx| { @@ -241,7 +243,8 @@ async fn should_update_all_installed_plugins() { #[tokio::test] async fn should_install_direct_local_plugin_with_deprecation_warning() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_install_direct_local_plugin_with_deprecation_warning", |ctx| { @@ -316,7 +319,8 @@ async fn should_install_direct_local_plugin_with_deprecation_warning() { #[tokio::test] async fn should_list_browse_refresh_and_remove_local_marketplace() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_list_browse_refresh_and_remove_local_marketplace", |ctx| { @@ -434,7 +438,8 @@ async fn should_list_browse_refresh_and_remove_local_marketplace() { #[tokio::test] async fn should_reload_mcp_config_cache() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_server_plugins", "should_reload_mcp_config_cache", |ctx| { @@ -538,3 +543,5 @@ fn single_plugin<'a>( assert_eq!(matches.len(), 1, "expected one plugin in {list:?}"); matches[0] } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_server_plugins", 7); diff --git a/rust/tests/e2e/rpc_server_remote_control.rs b/rust/tests/e2e/rpc_server_remote_control.rs index a49f1d12a..49809235c 100644 --- a/rust/tests/e2e/rpc_server_remote_control.rs +++ b/rust/tests/e2e/rpc_server_remote_control.rs @@ -6,11 +6,10 @@ use github_copilot_sdk::rpc::{ }; use serde_json::Value; -use super::support::with_e2e_context; - #[tokio::test] async fn should_report_remote_control_status_as_off() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_remote_control", "should_report_remote_control_status_as_off", |ctx| { @@ -34,7 +33,8 @@ async fn should_report_remote_control_status_as_off() { #[tokio::test] async fn should_treat_set_steering_as_no_op_when_off() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_remote_control", "should_treat_set_steering_as_no_op_when_off", |ctx| { @@ -60,7 +60,8 @@ async fn should_treat_set_steering_as_no_op_when_off() { #[tokio::test] async fn should_report_not_stopped_when_remote_control_is_off() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_remote_control", "should_report_not_stopped_when_remote_control_is_off", |ctx| { @@ -85,7 +86,8 @@ async fn should_report_not_stopped_when_remote_control_is_off() { #[tokio::test] async fn should_reject_transfer_when_off_with_compare_and_swap() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_remote_control", "should_reject_transfer_when_off_with_compare_and_swap", |ctx| { @@ -116,7 +118,8 @@ async fn should_reject_transfer_when_off_with_compare_and_swap() { #[tokio::test] async fn should_reach_runtime_when_starting_remote_control_for_unknown_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_server_remote_control", "should_reach_runtime_when_starting_remote_control_for_unknown_session", |ctx| { @@ -177,3 +180,5 @@ fn assert_not_unhandled(message: &str) { "{message}" ); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_server_remote_control", 5); diff --git a/rust/tests/e2e/rpc_session_state.rs b/rust/tests/e2e/rpc_session_state.rs index 629252190..01377780e 100644 --- a/rust/tests/e2e/rpc_session_state.rs +++ b/rust/tests/e2e/rpc_session_state.rs @@ -17,15 +17,14 @@ use github_copilot_sdk::session_events::{ }; use serde_json::json; -use super::support::{ - assistant_message_content, wait_for_condition, wait_for_event, with_e2e_context, -}; +use super::support::{assistant_message_content, wait_for_condition, wait_for_event}; const MODEL_ID: &str = "claude-sonnet-4.5"; #[tokio::test] async fn should_call_session_rpc_model_getcurrent() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_call_session_rpc_model_getcurrent", |ctx| { @@ -55,7 +54,8 @@ async fn should_call_session_rpc_model_getcurrent() { #[tokio::test] async fn should_call_session_rpc_model_switchto() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "rpc_session_state", "should_call_session_rpc_model_switchto", |ctx| { @@ -107,7 +107,8 @@ async fn should_call_session_rpc_model_switchto() { #[tokio::test] async fn should_get_and_set_session_mode() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_get_and_set_session_mode", |ctx| { @@ -146,7 +147,8 @@ async fn should_get_and_set_session_mode() { #[tokio::test] async fn should_shutdown_session_with_routine_type() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_shutdown_session_with_routine_type", |ctx| { @@ -184,7 +186,8 @@ async fn should_shutdown_session_with_routine_type() { #[tokio::test] async fn should_set_and_get_each_session_mode_value() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_set_and_get_each_session_mode_value", |ctx| { @@ -220,7 +223,8 @@ async fn should_set_and_get_each_session_mode_value() { #[tokio::test] async fn should_read_update_and_delete_plan() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_read_update_and_delete_plan", |ctx| { @@ -285,7 +289,8 @@ async fn should_read_update_and_delete_plan() { #[tokio::test] async fn should_call_workspace_file_rpc_methods() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_call_workspace_file_rpc_methods", |ctx| { @@ -342,7 +347,8 @@ async fn should_call_workspace_file_rpc_methods() { #[tokio::test] async fn should_reject_workspace_file_path_traversal() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_reject_workspace_file_path_traversal", |ctx| { @@ -386,7 +392,8 @@ async fn should_reject_workspace_file_path_traversal() { #[tokio::test] async fn should_create_workspace_file_with_nested_path_auto_creating_dirs() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_create_workspace_file_with_nested_path_auto_creating_dirs", |ctx| { @@ -428,7 +435,8 @@ async fn should_create_workspace_file_with_nested_path_auto_creating_dirs() { #[tokio::test] async fn should_report_error_reading_nonexistent_workspace_file() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_report_error_reading_nonexistent_workspace_file", |ctx| { @@ -461,7 +469,8 @@ async fn should_report_error_reading_nonexistent_workspace_file() { #[tokio::test] async fn should_update_existing_workspace_file_with_update_operation() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_update_existing_workspace_file_with_update_operation", |ctx| { @@ -516,7 +525,8 @@ async fn should_update_existing_workspace_file_with_update_operation() { #[tokio::test] async fn should_reject_empty_or_whitespace_session_name() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_reject_empty_or_whitespace_session_name", |ctx| { @@ -551,7 +561,8 @@ async fn should_reject_empty_or_whitespace_session_name() { #[tokio::test] async fn should_emit_title_changed_event_each_time_name_set_is_called() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_emit_title_changed_event_each_time_name_set_is_called", |ctx| { @@ -602,7 +613,8 @@ async fn should_emit_title_changed_event_each_time_name_set_is_called() { #[tokio::test] async fn should_get_and_set_session_metadata() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_call_metadata_snapshot_setworkingdirectory_and_recordcontextchange", |ctx| { @@ -651,7 +663,8 @@ async fn should_get_and_set_session_metadata() { #[tokio::test] async fn should_call_metadata_snapshot_setworkingdirectory_and_recordcontextchange() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_get_and_set_session_metadata", |ctx| { @@ -731,7 +744,8 @@ async fn should_call_metadata_snapshot_setworkingdirectory_and_recordcontextchan #[tokio::test] async fn should_update_options_and_initialize_session_services() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_update_options_and_initialize_session_services", |ctx| { @@ -796,7 +810,8 @@ async fn should_update_options_and_initialize_session_services() { #[tokio::test] async fn should_set_reasoningeffort_and_auto_name() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_set_reasoningeffort_and_auto_name", |ctx| { @@ -854,51 +869,57 @@ async fn should_set_reasoningeffort_and_auto_name() { #[tokio::test] async fn should_set_auth_credentials() { - with_e2e_context("rpc_session_state", "should_set_auth_credentials", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let token = "rpc-session-auth-token"; - ctx.set_copilot_user_by_token_with_login(token, "rpc-session-user"); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - - let set = session - .rpc() - .git_hub_auth() - .set_credentials(SessionSetCredentialsParams { - credentials: Some(json!({ - "type": "user", - "host": "github.com", - "login": "rpc-session-user" - })), - }) - .await - .expect("set credentials"); - assert!(set.success); - let status = session - .rpc() - .git_hub_auth() - .get_status() - .await - .expect("auth status"); - assert!(status.is_authenticated); - assert_eq!(status.auth_type, Some(AuthInfoType::User)); - assert_eq!(status.host.as_deref(), Some("github.com")); - assert_eq!(status.login.as_deref(), Some("rpc-session-user")); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "rpc_session_state", + "should_set_auth_credentials", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let token = "rpc-session-auth-token"; + ctx.set_copilot_user_by_token_with_login(token, "rpc-session-user"); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + + let set = session + .rpc() + .git_hub_auth() + .set_credentials(SessionSetCredentialsParams { + credentials: Some(json!({ + "type": "user", + "host": "github.com", + "login": "rpc-session-user" + })), + }) + .await + .expect("set credentials"); + assert!(set.success); + let status = session + .rpc() + .git_hub_auth() + .get_status() + .await + .expect("auth status"); + assert!(status.is_authenticated); + assert_eq!(status.auth_type, Some(AuthInfoType::User)); + assert_eq!(status.host.as_deref(), Some("github.com")); + assert_eq!(status.login.as_deref(), Some("rpc-session-user")); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_fork_session_with_persisted_messages() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_fork_session_with_persisted_messages", |ctx| { @@ -956,7 +977,8 @@ async fn should_fork_session_with_persisted_messages() { #[tokio::test] async fn should_report_error_when_forking_session_to_unknown_event_id() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_report_error_when_forking_session_to_unknown_event_id", |ctx| { @@ -992,7 +1014,8 @@ async fn should_report_error_when_forking_session_to_unknown_event_id() { #[tokio::test] async fn should_call_session_usage_and_permission_rpcs() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_call_session_usage_and_permission_rpcs", |ctx| { @@ -1039,7 +1062,8 @@ async fn should_call_session_usage_and_permission_rpcs() { #[tokio::test] async fn should_report_implemented_errors_for_unsupported_session_rpc_paths() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_report_implemented_errors_for_unsupported_session_rpc_paths", |ctx| { @@ -1075,7 +1099,8 @@ async fn should_report_implemented_errors_for_unsupported_session_rpc_paths() { #[tokio::test] async fn should_compact_session_history_after_messages() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state", "should_compact_session_history_after_messages", |ctx| { @@ -1181,3 +1206,5 @@ fn assistant_message_content_if_present( None } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_session_state", 22); diff --git a/rust/tests/e2e/rpc_session_state_extras.rs b/rust/tests/e2e/rpc_session_state_extras.rs index 749e9706d..f43359f0b 100644 --- a/rust/tests/e2e/rpc_session_state_extras.rs +++ b/rust/tests/e2e/rpc_session_state_extras.rs @@ -54,7 +54,8 @@ async fn should_list_models_for_session() { #[tokio::test] async fn should_report_session_activity_when_idle() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_report_session_activity_when_idle", |ctx| { @@ -86,7 +87,8 @@ async fn should_report_session_activity_when_idle() { #[tokio::test] async fn should_get_and_set_allowall_permissions() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_get_and_set_allowall_permissions", |ctx| { @@ -162,7 +164,8 @@ async fn should_get_and_set_allowall_permissions() { #[tokio::test] async fn should_read_empty_sql_todos_for_fresh_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_read_empty_sql_todos_for_fresh_session", |ctx| { @@ -193,7 +196,8 @@ async fn should_read_empty_sql_todos_for_fresh_session() { #[tokio::test] async fn should_get_telemetry_engagement_id() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_get_telemetry_engagement_id", |ctx| { @@ -222,7 +226,8 @@ async fn should_get_telemetry_engagement_id() { #[tokio::test] async fn should_get_current_tool_metadata_after_initialization() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_get_current_tool_metadata_after_initialization", |ctx| { @@ -262,7 +267,8 @@ async fn should_get_current_tool_metadata_after_initialization() { #[tokio::test] async fn should_add_byok_provider_and_model_at_runtime() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_add_byok_provider_and_model_at_runtime", |ctx| { @@ -342,7 +348,8 @@ async fn should_add_byok_provider_and_model_at_runtime() { #[tokio::test] async fn should_return_empty_completions_when_host_does_not_provide_them() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_return_empty_completions_when_host_does_not_provide_them", |ctx| { @@ -375,7 +382,8 @@ async fn should_return_empty_completions_when_host_does_not_provide_them() { #[tokio::test] async fn should_report_visibility_as_unsynced_for_local_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_report_visibility_as_unsynced_for_local_session", |ctx| { @@ -418,7 +426,8 @@ async fn should_report_visibility_as_unsynced_for_local_session() { #[tokio::test] async fn should_get_context_attribution_and_heaviest_messages_after_turn() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_get_context_attribution_and_heaviest_messages_after_turn", |ctx| { @@ -464,7 +473,8 @@ async fn should_get_context_attribution_and_heaviest_messages_after_turn() { #[tokio::test] async fn should_update_and_clear_live_subagent_settings() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_update_and_clear_live_subagent_settings", |ctx| { @@ -515,7 +525,8 @@ async fn should_update_and_clear_live_subagent_settings() { #[tokio::test] async fn should_reload_session_plugins() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_session_state_extras", "should_reload_session_plugins", |ctx| { @@ -554,3 +565,5 @@ async fn should_reload_session_plugins() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_session_state_extras", 11); diff --git a/rust/tests/e2e/rpc_shell_and_fleet.rs b/rust/tests/e2e/rpc_shell_and_fleet.rs index 219929c44..968d51147 100644 --- a/rust/tests/e2e/rpc_shell_and_fleet.rs +++ b/rust/tests/e2e/rpc_shell_and_fleet.rs @@ -1,10 +1,11 @@ use github_copilot_sdk::rpc::{ShellExecRequest, ShellKillRequest}; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn should_execute_shell_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_and_fleet", "should_execute_shell_command", |ctx| { @@ -41,42 +42,47 @@ async fn should_execute_shell_command() { #[tokio::test] async fn should_kill_shell_process() { - with_e2e_context("rpc_shell_and_fleet", "should_kill_shell_process", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "rpc_shell_and_fleet", + "should_kill_shell_process", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - let exec = session - .rpc() - .shell() - .exec(ShellExecRequest { - command: long_running_command(), - cwd: Some(ctx.work_dir().display().to_string()), - timeout: None, - }) - .await - .expect("start shell process"); - assert!(!exec.process_id.trim().is_empty()); + let exec = session + .rpc() + .shell() + .exec(ShellExecRequest { + command: long_running_command(), + cwd: Some(ctx.work_dir().display().to_string()), + timeout: None, + }) + .await + .expect("start shell process"); + assert!(!exec.process_id.trim().is_empty()); - let killed = session - .rpc() - .shell() - .kill(ShellKillRequest { - process_id: exec.process_id, - signal: None, - }) - .await - .expect("kill shell process"); - assert!(killed.killed); + let killed = session + .rpc() + .shell() + .kill(ShellKillRequest { + process_id: exec.process_id, + signal: None, + }) + .await + .expect("kill shell process"); + assert!(killed.killed); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } @@ -113,3 +119,5 @@ fn long_running_command() -> String { fn long_running_command() -> String { "sleep 30".to_string() } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_shell_and_fleet", 2); diff --git a/rust/tests/e2e/rpc_shell_edge_cases.rs b/rust/tests/e2e/rpc_shell_edge_cases.rs index 318a7e500..df5ddb1dc 100644 --- a/rust/tests/e2e/rpc_shell_edge_cases.rs +++ b/rust/tests/e2e/rpc_shell_edge_cases.rs @@ -3,11 +3,12 @@ use std::time::Duration; use github_copilot_sdk::rpc::{ShellExecRequest, ShellKillRequest, ShellKillSignal}; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn shell_exec_with_timeout_kills_long_running_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_exec_with_timeout_kills_long_running_command", |ctx| { @@ -58,7 +59,8 @@ async fn shell_exec_with_timeout_kills_long_running_command() { #[tokio::test] async fn shell_exec_with_custom_cwd_honors_override() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_exec_with_custom_cwd_honors_override", |ctx| { @@ -97,7 +99,8 @@ async fn shell_exec_with_custom_cwd_honors_override() { #[tokio::test] async fn shell_exec_with_nonexistent_command_returns_processid_and_cleans_up() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_exec_with_nonexistent_command_returns_processid_and_cleans_up", |ctx| { @@ -133,7 +136,8 @@ async fn shell_exec_with_nonexistent_command_returns_processid_and_cleans_up() { #[tokio::test] async fn shell_kill_unknown_processid_returns_false() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_kill_unknown_processid_returns_false", |ctx| { @@ -167,7 +171,8 @@ async fn shell_kill_unknown_processid_returns_false() { #[tokio::test] async fn shell_kill_cleans_up_after_terminating_signal() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_kill_cleans_up_after_terminating_signal", |ctx| { @@ -212,7 +217,8 @@ async fn shell_kill_cleans_up_after_terminating_signal() { #[tokio::test] async fn shell_exec_with_stderr_output_cleans_up() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_exec_with_stderr_output_cleans_up", |ctx| { @@ -249,7 +255,8 @@ async fn shell_exec_with_stderr_output_cleans_up() { #[tokio::test] async fn shell_exec_with_large_stdout_cleans_up() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_edge_cases", "shell_exec_with_large_stdout_cleans_up", |ctx| { @@ -407,3 +414,5 @@ fn large_stdout_command(marker_path: &Path) -> String { marker_path.display() ) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_shell_edge_cases", 7); diff --git a/rust/tests/e2e/rpc_shell_user_requested.rs b/rust/tests/e2e/rpc_shell_user_requested.rs index 7bd52ae9f..43de1c2cc 100644 --- a/rust/tests/e2e/rpc_shell_user_requested.rs +++ b/rust/tests/e2e/rpc_shell_user_requested.rs @@ -5,11 +5,12 @@ use std::time::Duration; use github_copilot_sdk::RequestId; use github_copilot_sdk::rpc::{ShellCancelUserRequestedRequest, ShellExecuteUserRequestedRequest}; -use super::support::{wait_for_condition, with_e2e_context}; +use super::support::wait_for_condition; #[tokio::test] async fn should_execute_user_requested_shell_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_user_requested", "should_execute_user_requested_shell_command", |ctx| { @@ -52,7 +53,8 @@ async fn should_execute_user_requested_shell_command() { #[tokio::test] async fn should_cancel_user_requested_shell_command() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_shell_user_requested", "should_cancel_user_requested_shell_command", |ctx| { @@ -171,3 +173,5 @@ fn powershell_quote(path: &Path) -> String { fn posix_shell_quote(path: &Path) -> String { format!("'{}'", path.display().to_string().replace('\'', "'\\''")) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_shell_user_requested", 2); diff --git a/rust/tests/e2e/rpc_tasks_and_handlers.rs b/rust/tests/e2e/rpc_tasks_and_handlers.rs index 6d15d75b4..b3010ab78 100644 --- a/rust/tests/e2e/rpc_tasks_and_handlers.rs +++ b/rust/tests/e2e/rpc_tasks_and_handlers.rs @@ -27,11 +27,10 @@ use github_copilot_sdk::rpc::{ UIUnregisterDirectAutoModeSwitchHandlerRequest, UIUserInputResponse, }; -use super::support::with_e2e_context; - #[tokio::test] async fn should_list_task_state_and_return_false_for_missing_task_operations() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_tasks_and_handlers", "should_list_task_state_and_return_false_for_missing_task_operations", |ctx| { @@ -145,7 +144,8 @@ async fn should_list_task_state_and_return_false_for_missing_task_operations() { #[tokio::test] async fn should_report_implemented_error_for_missing_task_agent_type() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_tasks_and_handlers", "should_report_implemented_error_for_missing_task_agent_type", |ctx| { @@ -182,7 +182,8 @@ async fn should_report_implemented_error_for_missing_task_agent_type() { #[tokio::test] async fn should_report_implemented_error_for_invalid_task_agent_model() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_tasks_and_handlers", "should_report_implemented_error_for_invalid_task_agent_model", |ctx| { @@ -229,7 +230,7 @@ async fn should_report_implemented_error_for_invalid_task_agent_model() { #[tokio::test] async fn should_return_expected_results_for_missing_pending_handler_requestids() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "rpc_tasks_and_handlers", "should_return_expected_results_for_missing_pending_handler_requestids", |ctx| { @@ -443,7 +444,8 @@ async fn should_return_expected_results_for_missing_pending_handler_requestids() #[tokio::test] async fn should_register_and_unregister_direct_auto_mode_switch_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_tasks_and_handlers", "should_register_and_unregister_direct_auto_mode_switch_handler", |ctx| { @@ -503,3 +505,5 @@ fn assert_implemented_error(result: Result, met "expected implemented error for {method}, got {message}" ); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_tasks_and_handlers", 5); diff --git a/rust/tests/e2e/rpc_ui_ephemeral_query.rs b/rust/tests/e2e/rpc_ui_ephemeral_query.rs index 83852d092..2fa421cc6 100644 --- a/rust/tests/e2e/rpc_ui_ephemeral_query.rs +++ b/rust/tests/e2e/rpc_ui_ephemeral_query.rs @@ -1,10 +1,9 @@ use github_copilot_sdk::rpc::UIEphemeralQueryRequest; -use super::support::with_e2e_context; - #[tokio::test] async fn should_answer_ephemeral_query() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_ui_ephemeral_query", "should_answer_ephemeral_query", |ctx| { @@ -36,3 +35,5 @@ async fn should_answer_ephemeral_query() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_ui_ephemeral_query", 1); diff --git a/rust/tests/e2e/rpc_workspace_checkpoints.rs b/rust/tests/e2e/rpc_workspace_checkpoints.rs index 0a8bf5615..48145970c 100644 --- a/rust/tests/e2e/rpc_workspace_checkpoints.rs +++ b/rust/tests/e2e/rpc_workspace_checkpoints.rs @@ -6,11 +6,10 @@ use github_copilot_sdk::rpc::{ WorkspacesReadCheckpointRequest, WorkspacesReadFileRequest, WorkspacesSaveLargePasteRequest, }; -use super::support::with_e2e_context; - #[tokio::test] async fn should_list_no_checkpoints_for_fresh_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_workspace_checkpoints", "should_list_no_checkpoints_for_fresh_session", |ctx| { @@ -40,13 +39,16 @@ async fn should_list_no_checkpoints_for_fresh_session() { #[tokio::test] async fn should_return_null_or_empty_content_for_unknown_checkpoint() { - // In-process, session.workspaces.readCheckpoint is answered by the native runtime, - // which decodes the checkpoint number as a u32 and rejects the i64::MAX sentinel this - // test uses. Covered by the default (stdio) transport. See issue #1934. - if super::support::skip_inprocess("readCheckpoint decodes the id as u32 in-process") { + if super::support::skip_shared_e2e_inprocess( + &E2E, + "readCheckpoint decodes the id as u32 in-process", + ) + .await + { return; } - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_workspace_checkpoints", "should_return_null_or_empty_content_for_unknown_checkpoint", |ctx| { @@ -76,7 +78,8 @@ async fn should_return_null_or_empty_content_for_unknown_checkpoint() { #[tokio::test] async fn should_return_typed_workspace_diff_result() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_workspace_checkpoints", "should_return_typed_workspace_diff_result", |ctx| { @@ -128,7 +131,8 @@ async fn should_return_typed_workspace_diff_result() { #[tokio::test] async fn should_save_large_paste_and_expose_readable_content() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "rpc_workspace_checkpoints", "should_save_large_paste_and_expose_readable_content", |ctx| { @@ -188,3 +192,5 @@ fn init_git_repository(path: &Path) { .expect("run git init"); assert!(status.success(), "git init should succeed"); } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("rpc_workspace_checkpoints", 4); diff --git a/rust/tests/e2e/session.rs b/rust/tests/e2e/session.rs index f66c7e772..e2ca76c47 100644 --- a/rust/tests/e2e/session.rs +++ b/rust/tests/e2e/session.rs @@ -21,39 +21,44 @@ use serde_json::json; use super::support::{ assert_uuid_like, assistant_message_content, collect_until_idle, event_types, - get_system_message, get_tool_names, wait_for_condition, wait_for_event, with_e2e_context, + get_system_message, get_tool_names, wait_for_condition, wait_for_event, }; #[tokio::test] async fn shouldcreateanddisconnectsessions() { - with_e2e_context("session", "shouldcreateanddisconnectsessions", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - ctx.approve_all_session_config() - .with_model("claude-sonnet-4.5"), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "shouldcreateanddisconnectsessions", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_model("claude-sonnet-4.5"), + ) + .await + .expect("create session"); - assert_uuid_like(session.id()); - let messages = session.get_events().await.expect("get messages"); - assert!(!messages.is_empty(), "expected initial session events"); - let start = messages[0] - .typed_data::() - .expect("session.start data"); - assert_eq!(start.session_id, session.id().clone()); + assert_uuid_like(session.id()); + let messages = session.get_events().await.expect("get messages"); + assert!(!messages.is_empty(), "expected initial session events"); + let start = messages[0] + .typed_data::() + .expect("session.start data"); + assert_eq!(start.session_id, session.id().clone()); - session.disconnect().await.expect("disconnect session"); - assert!( - session.get_events().await.is_err(), - "disconnected session should no longer serve message history" - ); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + assert!( + session.get_events().await.is_err(), + "disconnected session should no longer serve message history" + ); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } @@ -88,39 +93,45 @@ async fn disposeasync_from_handler_does_not_deadlock() { #[tokio::test] async fn should_have_stateful_conversation() { - with_e2e_context("session", "should_have_stateful_conversation", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_have_stateful_conversation", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - let first = session - .send_and_wait("What is 1+1?") - .await - .expect("first send") - .expect("first assistant message"); - assert!(assistant_message_content(&first).contains('2')); + let first = session + .send_and_wait("What is 1+1?") + .await + .expect("first send") + .expect("first assistant message"); + assert!(assistant_message_content(&first).contains('2')); - let second = session - .send_and_wait("Now if you double that, what do you get?") - .await - .expect("second send") - .expect("second assistant message"); - assert!(assistant_message_content(&second).contains('4')); + let second = session + .send_and_wait("Now if you double that, what do you get?") + .await + .expect("second send") + .expect("second assistant message"); + assert!(assistant_message_content(&second).contains('4')); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_create_a_session_with_appended_systemmessage_config() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_appended_systemmessage_config", |ctx| { @@ -164,7 +175,8 @@ async fn should_create_a_session_with_appended_systemmessage_config() { #[tokio::test] async fn should_create_a_session_with_replaced_systemmessage_config() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_replaced_systemmessage_config", |ctx| { @@ -206,7 +218,8 @@ async fn should_create_a_session_with_replaced_systemmessage_config() { #[tokio::test] async fn should_create_a_session_with_customized_systemmessage_config() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_customized_systemmessage_config", |ctx| { @@ -260,7 +273,8 @@ async fn should_create_a_session_with_customized_systemmessage_config() { #[tokio::test] async fn should_create_a_session_with_availabletools() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_availabletools", |ctx| { @@ -296,7 +310,8 @@ async fn should_create_a_session_with_availabletools() { #[tokio::test] async fn should_create_a_session_with_excludedtools() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_excludedtools", |ctx| { @@ -332,7 +347,8 @@ async fn should_create_a_session_with_excludedtools() { #[tokio::test] async fn should_create_a_session_with_defaultagent_excludedtools() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_a_session_with_defaultagent_excludedtools", |ctx| { @@ -371,37 +387,43 @@ async fn should_create_a_session_with_defaultagent_excludedtools() { #[tokio::test] async fn should_create_session_with_custom_tool() { - with_e2e_context("session", "should_create_session_with_custom_tool", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(super::support::DEFAULT_TEST_TOKEN) - .with_permission_handler(Arc::new(ApproveAllHandler)) - .with_tools(vec![secret_number_tool()]), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_create_session_with_custom_tool", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session( + SessionConfig::default() + .with_github_token(super::support::DEFAULT_TEST_TOKEN) + .with_permission_handler(Arc::new(ApproveAllHandler)) + .with_tools(vec![secret_number_tool()]), + ) + .await + .expect("create session"); - let answer = session - .send_and_wait("What is the secret number for key ALPHA?") - .await - .expect("send") - .expect("assistant message"); - assert!(assistant_message_content(&answer).contains("54321")); + let answer = session + .send_and_wait("What is the secret number for key ALPHA?") + .await + .expect("send") + .expect("assistant message"); + assert!(assistant_message_content(&answer).contains("54321")); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_throw_error_when_resuming_non_existent_session() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_throw_error_when_resuming_non_existent_session", |ctx| { @@ -425,7 +447,7 @@ async fn should_throw_error_when_resuming_non_existent_session() { #[tokio::test] async fn should_abort_a_session() { - with_e2e_context("session", "should_abort_a_session", |ctx| { + super::support::with_shared_e2e_context(&E2E, "session", "should_abort_a_session", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -479,7 +501,8 @@ async fn should_abort_a_session() { #[tokio::test] async fn should_resume_a_session_using_the_same_client() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_resume_a_session_using_the_same_client", |ctx| { @@ -535,7 +558,7 @@ async fn should_resume_a_session_using_the_same_client() { #[tokio::test] async fn should_resume_a_session_using_a_new_client() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "session", "should_resume_a_session_using_a_new_client", |ctx| { @@ -607,7 +630,7 @@ async fn should_resume_a_session_using_a_new_client() { #[tokio::test] async fn resumes_a_persisted_session_from_a_new_client_when_an_mcp_oauth_handler_is_configured() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "session", "resumes_a_persisted_session_from_a_new_client_when_an_mcp_oauth_handler_is_configured", |ctx| { @@ -661,38 +684,44 @@ async fn resumes_a_persisted_session_from_a_new_client_when_an_mcp_oauth_handler #[tokio::test] async fn should_receive_session_events() { - with_e2e_context("session", "should_receive_session_events", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_receive_session_events", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - let events = session.subscribe(); - let answer = session - .send_and_wait("What is 100+200?") - .await - .expect("send") - .expect("assistant message"); - assert!(assistant_message_content(&answer).contains("300")); - let observed = collect_until_idle(events).await; - let types = event_types(&observed); - assert!(types.contains(&"user.message")); - assert!(types.contains(&"assistant.message")); - assert!(types.contains(&"session.idle")); + let events = session.subscribe(); + let answer = session + .send_and_wait("What is 100+200?") + .await + .expect("send") + .expect("assistant message"); + assert!(assistant_message_content(&answer).contains("300")); + let observed = collect_until_idle(events).await; + let types = event_types(&observed); + assert!(types.contains(&"user.message")); + assert!(types.contains(&"assistant.message")); + assert!(types.contains(&"session.idle")); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn send_returns_immediately_while_events_stream_in_background() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "send_returns_immediately_while_events_stream_in_background", |ctx| { @@ -731,7 +760,8 @@ async fn send_returns_immediately_while_events_stream_in_background() { #[tokio::test] async fn sendandwait_blocks_until_session_idle_and_returns_final_assistant_message() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "sendandwait_blocks_until_session_idle_and_returns_final_assistant_message", |ctx| { @@ -767,127 +797,143 @@ async fn sendandwait_blocks_until_session_idle_and_returns_final_assistant_messa #[tokio::test] async fn should_list_sessions_with_context() { - with_e2e_context("session", "should_list_sessions_with_context", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let session_id = session.id().clone(); - - session.send_and_wait("Say OK.").await.expect("send"); - wait_for_condition("session to appear in list", || { - let client = client.clone(); - let session_id = session_id.clone(); - async move { - client.list_sessions(None).await.is_ok_and(|sessions| { - sessions - .iter() - .any(|session| session.session_id == session_id) - }) - } - }) - .await; + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_list_sessions_with_context", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let session_id = session.id().clone(); - let all_sessions = client.list_sessions(None).await.expect("list sessions"); - assert!(!all_sessions.is_empty()); + session.send_and_wait("Say OK.").await.expect("send"); + wait_for_condition("session to appear in list", || { + let client = client.clone(); + let session_id = session_id.clone(); + async move { + client.list_sessions(None).await.is_ok_and(|sessions| { + sessions + .iter() + .any(|session| session.session_id == session_id) + }) + } + }) + .await; - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + let all_sessions = client.list_sessions(None).await.expect("list sessions"); + assert!(!all_sessions.is_empty()); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_get_session_metadata_by_id() { - with_e2e_context("session", "should_get_session_metadata_by_id", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let session_id = session.id().clone(); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_get_session_metadata_by_id", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let session_id = session.id().clone(); + + session.send_and_wait("Say hello").await.expect("send"); + wait_for_condition("session metadata to persist", || { + let client = client.clone(); + let session_id = session_id.clone(); + async move { + client + .get_session_metadata(&session_id) + .await + .is_ok_and(|metadata| metadata.is_some()) + } + }) + .await; - session.send_and_wait("Say hello").await.expect("send"); - wait_for_condition("session metadata to persist", || { - let client = client.clone(); - let session_id = session_id.clone(); - async move { + let metadata = client + .get_session_metadata(&session_id) + .await + .expect("get metadata") + .expect("session metadata"); + assert_eq!(metadata.session_id, session_id); + assert!(!metadata.start_time.is_empty()); + assert!(!metadata.modified_time.is_empty()); + assert!( client - .get_session_metadata(&session_id) + .get_session_metadata(&github_copilot_sdk::SessionId::new( + "non-existent-session-id" + )) .await - .is_ok_and(|metadata| metadata.is_some()) - } - }) - .await; - - let metadata = client - .get_session_metadata(&session_id) - .await - .expect("get metadata") - .expect("session metadata"); - assert_eq!(metadata.session_id, session_id); - assert!(!metadata.start_time.is_empty()); - assert!(!metadata.modified_time.is_empty()); - assert!( - client - .get_session_metadata(&github_copilot_sdk::SessionId::new( - "non-existent-session-id" - )) - .await - .expect("get missing metadata") - .is_none() - ); + .expect("get missing metadata") + .is_none() + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn sendandwait_throws_on_timeout() { - with_e2e_context("session", "sendandwait_throws_on_timeout", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let idle = tokio::spawn(wait_for_event( - session.subscribe(), - "session.idle after timeout abort", - |event| event.parsed_type() == SessionEventType::SessionIdle, - )); - - let error = session - .send_and_wait( - MessageOptions::new("Run 'sleep 2 && echo done'") - .with_wait_timeout(Duration::from_millis(100)), - ) - .await - .expect_err("send_and_wait should time out"); - assert!(error.to_string().contains("timed out")); + super::support::with_shared_e2e_context( + &E2E, + "session", + "sendandwait_throws_on_timeout", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let idle = tokio::spawn(wait_for_event( + session.subscribe(), + "session.idle after timeout abort", + |event| event.parsed_type() == SessionEventType::SessionIdle, + )); + + let error = session + .send_and_wait( + MessageOptions::new("Run 'sleep 2 && echo done'") + .with_wait_timeout(Duration::from_millis(100)), + ) + .await + .expect_err("send_and_wait should time out"); + assert!(error.to_string().contains("timed out")); - session.abort().await.expect("abort session"); - idle.await.expect("idle task"); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.abort().await.expect("abort session"); + idle.await.expect("idle task"); + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_create_session_with_custom_config_dir() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "session", "should_create_session_with_custom_config_dir", |ctx| { @@ -921,183 +967,198 @@ async fn should_create_session_with_custom_config_dir() { #[tokio::test] async fn should_set_model_on_existing_session() { - with_e2e_context("session", "should_set_model_on_existing_session", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let model_changed = tokio::spawn(wait_for_event( - session.subscribe(), - "session.model_change", - |event| event.parsed_type() == SessionEventType::SessionModelChange, - )); - - session.set_model("gpt-4.1", None).await.expect("set model"); - let event = model_changed.await.expect("model change task"); - let data = event - .typed_data::() - .expect("session.model_change data"); - assert_eq!(data.new_model, "gpt-4.1"); - - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_set_model_on_existing_session", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let model_changed = tokio::spawn(wait_for_event( + session.subscribe(), + "session.model_change", + |event| event.parsed_type() == SessionEventType::SessionModelChange, + )); + + session.set_model("gpt-4.1", None).await.expect("set model"); + let event = model_changed.await.expect("model change task"); + let data = event + .typed_data::() + .expect("session.model_change data"); + assert_eq!(data.new_model, "gpt-4.1"); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_set_model_with_reasoningeffort() { - with_e2e_context("session", "should_set_model_with_reasoningeffort", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let model_changed = tokio::spawn(wait_for_event( - session.subscribe(), - "session.model_change with reasoning effort", - |event| event.parsed_type() == SessionEventType::SessionModelChange, - )); + super::support::with_dedicated_group_e2e_context( + &E2E, + "session", + "should_set_model_with_reasoningeffort", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let model_changed = tokio::spawn(wait_for_event( + session.subscribe(), + "session.model_change with reasoning effort", + |event| event.parsed_type() == SessionEventType::SessionModelChange, + )); - session - .set_model( - "gpt-5.4", - Some(SetModelOptions::default().with_reasoning_effort("high")), - ) - .await - .expect("set model"); - let event = model_changed.await.expect("model change task"); - let data = event - .typed_data::() - .expect("session.model_change data"); - assert_eq!(data.new_model, "gpt-5.4"); - assert_eq!(data.reasoning_effort.as_deref(), Some("high")); + session + .set_model( + "gpt-5.4", + Some(SetModelOptions::default().with_reasoning_effort("high")), + ) + .await + .expect("set model"); + let event = model_changed.await.expect("model change task"); + let data = event + .typed_data::() + .expect("session.model_change data"); + assert_eq!(data.new_model, "gpt-5.4"); + assert_eq!(data.reasoning_effort.as_deref(), Some("high")); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_log_messages_at_various_levels() { - with_e2e_context("session", "should_log_messages_at_various_levels", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let mut events = session.subscribe(); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_log_messages_at_various_levels", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let mut events = session.subscribe(); - session.log("Info message", None).await.expect("info log"); - session - .log( - "Warning message", - Some(LogOptions::default().with_level(SessionLogLevel::Warning)), - ) - .await - .expect("warning log"); - session - .log( - "Error message", - Some(LogOptions::default().with_level(SessionLogLevel::Error)), - ) - .await - .expect("error log"); - session - .log( - "Ephemeral message", - Some(LogOptions::default().with_ephemeral(true)), - ) - .await - .expect("ephemeral log"); - - let mut observed = Vec::new(); - tokio::time::timeout(Duration::from_secs(10), async { - while observed.len() < 4 { - let event = events.recv().await.expect("session event"); - if matches!( - event.parsed_type(), - SessionEventType::SessionInfo - | SessionEventType::SessionWarning - | SessionEventType::SessionError - ) { - observed.push(event); + session.log("Info message", None).await.expect("info log"); + session + .log( + "Warning message", + Some(LogOptions::default().with_level(SessionLogLevel::Warning)), + ) + .await + .expect("warning log"); + session + .log( + "Error message", + Some(LogOptions::default().with_level(SessionLogLevel::Error)), + ) + .await + .expect("error log"); + session + .log( + "Ephemeral message", + Some(LogOptions::default().with_ephemeral(true)), + ) + .await + .expect("ephemeral log"); + + let mut observed = Vec::new(); + tokio::time::timeout(Duration::from_secs(10), async { + while observed.len() < 4 { + let event = events.recv().await.expect("session event"); + if matches!( + event.parsed_type(), + SessionEventType::SessionInfo + | SessionEventType::SessionWarning + | SessionEventType::SessionError + ) { + observed.push(event); + } } - } - }) - .await - .expect("log events"); - - let info = observed - .iter() - .find(|event| { - event - .typed_data::() - .is_some_and(|data| data.message == "Info message") }) - .expect("info message"); - assert_eq!( - info.typed_data::() - .expect("info data") - .info_type, - "notification" - ); - let warning = observed - .iter() - .find(|event| { - event + .await + .expect("log events"); + + let info = observed + .iter() + .find(|event| { + event + .typed_data::() + .is_some_and(|data| data.message == "Info message") + }) + .expect("info message"); + assert_eq!( + info.typed_data::() + .expect("info data") + .info_type, + "notification" + ); + let warning = observed + .iter() + .find(|event| { + event + .typed_data::() + .is_some_and(|data| data.message == "Warning message") + }) + .expect("warning message"); + assert_eq!( + warning .typed_data::() - .is_some_and(|data| data.message == "Warning message") - }) - .expect("warning message"); - assert_eq!( - warning - .typed_data::() - .expect("warning data") - .warning_type, - "notification" - ); - let error = observed - .iter() - .find(|event| { - event + .expect("warning data") + .warning_type, + "notification" + ); + let error = observed + .iter() + .find(|event| { + event + .typed_data::() + .is_some_and(|data| data.message == "Error message") + }) + .expect("error message"); + assert_eq!( + error .typed_data::() - .is_some_and(|data| data.message == "Error message") - }) - .expect("error message"); - assert_eq!( - error - .typed_data::() - .expect("error data") - .error_type, - "notification" - ); - assert!(observed.iter().any(|event| { - event - .typed_data::() - .is_some_and(|data| data.message == "Ephemeral message") - })); + .expect("error data") + .error_type, + "notification" + ); + assert!(observed.iter().any(|event| { + event + .typed_data::() + .is_some_and(|data| data.message == "Ephemeral message") + })); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_accept_blob_attachments() { - with_e2e_context("session", "should_accept_blob_attachments", |ctx| { + super::support::with_shared_e2e_context(&E2E, "session", "should_accept_blob_attachments", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let png_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="; @@ -1139,197 +1200,213 @@ async fn should_accept_blob_attachments() { #[tokio::test] async fn should_send_with_file_attachment() { - with_e2e_context("session", "should_send_with_file_attachment", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let file_path = ctx.work_dir().join("attached-file.txt"); - std::fs::write(&file_path, "FILE_ATTACHMENT_SENTINEL").expect("write attached file"); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_send_with_file_attachment", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let file_path = ctx.work_dir().join("attached-file.txt"); + std::fs::write(&file_path, "FILE_ATTACHMENT_SENTINEL") + .expect("write attached file"); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - session - .send_and_wait( - MessageOptions::new("Read the attached file and reply with its contents.") - .with_attachments(vec![Attachment::File { - path: file_path.clone(), - display_name: Some("attached-file.txt".to_string()), - line_range: Some(AttachmentLineRange { start: 1, end: 1 }), - }]), - ) - .await - .expect("send"); + session + .send_and_wait( + MessageOptions::new("Read the attached file and reply with its contents.") + .with_attachments(vec![Attachment::File { + path: file_path.clone(), + display_name: Some("attached-file.txt".to_string()), + line_range: Some(AttachmentLineRange { start: 1, end: 1 }), + }]), + ) + .await + .expect("send"); - let user = latest_user_message(&session).await; - let attachments = user - .typed_data::() - .expect("user message data") - .attachments - .expect("attachments"); - assert_eq!(attachments.len(), 1); - assert_eq!( - attachments[0] - .get("displayName") - .and_then(serde_json::Value::as_str), - Some("attached-file.txt") - ); - assert_eq!( - attachments[0] - .get("path") - .and_then(serde_json::Value::as_str), - Some(file_path.to_string_lossy().as_ref()) - ); - assert_eq!( - attachments[0] - .get("lineRange") - .and_then(|value| value.get("start")) - .and_then(serde_json::Value::as_u64), - Some(1) - ); + let user = latest_user_message(&session).await; + let attachments = user + .typed_data::() + .expect("user message data") + .attachments + .expect("attachments"); + assert_eq!(attachments.len(), 1); + assert_eq!( + attachments[0] + .get("displayName") + .and_then(serde_json::Value::as_str), + Some("attached-file.txt") + ); + assert_eq!( + attachments[0] + .get("path") + .and_then(serde_json::Value::as_str), + Some(file_path.to_string_lossy().as_ref()) + ); + assert_eq!( + attachments[0] + .get("lineRange") + .and_then(|value| value.get("start")) + .and_then(serde_json::Value::as_u64), + Some(1) + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_send_with_directory_attachment() { - with_e2e_context("session", "should_send_with_directory_attachment", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let directory_path = ctx.work_dir().join("attached-directory"); - std::fs::create_dir(&directory_path).expect("create attached directory"); - std::fs::write( - directory_path.join("readme.txt"), - "DIRECTORY_ATTACHMENT_SENTINEL", - ) - .expect("write attached directory file"); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - - session - .send_and_wait( - MessageOptions::new("List the attached directory.").with_attachments(vec![ - Attachment::Directory { - path: directory_path.clone(), - display_name: Some("attached-directory".to_string()), - }, - ]), + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_send_with_directory_attachment", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let directory_path = ctx.work_dir().join("attached-directory"); + std::fs::create_dir(&directory_path).expect("create attached directory"); + std::fs::write( + directory_path.join("readme.txt"), + "DIRECTORY_ATTACHMENT_SENTINEL", ) - .await - .expect("send"); + .expect("write attached directory file"); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - let user = latest_user_message(&session).await; - let attachments = user - .typed_data::() - .expect("user message data") - .attachments - .expect("attachments"); - assert_eq!(attachments.len(), 1); - assert_eq!( - attachments[0] - .get("displayName") - .and_then(serde_json::Value::as_str), - Some("attached-directory") - ); - assert_eq!( - attachments[0] - .get("path") - .and_then(serde_json::Value::as_str), - Some(directory_path.to_string_lossy().as_ref()) - ); + session + .send_and_wait( + MessageOptions::new("List the attached directory.").with_attachments(vec![ + Attachment::Directory { + path: directory_path.clone(), + display_name: Some("attached-directory".to_string()), + }, + ]), + ) + .await + .expect("send"); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + let user = latest_user_message(&session).await; + let attachments = user + .typed_data::() + .expect("user message data") + .attachments + .expect("attachments"); + assert_eq!(attachments.len(), 1); + assert_eq!( + attachments[0] + .get("displayName") + .and_then(serde_json::Value::as_str), + Some("attached-directory") + ); + assert_eq!( + attachments[0] + .get("path") + .and_then(serde_json::Value::as_str), + Some(directory_path.to_string_lossy().as_ref()) + ); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_send_with_selection_attachment() { - with_e2e_context("session", "should_send_with_selection_attachment", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let file_path = std::path::PathBuf::from("selected-file.cs"); - let absolute_file_path = ctx.work_dir().join(&file_path); - std::fs::write( - &absolute_file_path, - "class C { string Value = \"SELECTION_SENTINEL\"; }", - ) - .expect("write selection file"); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_send_with_selection_attachment", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let file_path = std::path::PathBuf::from("selected-file.cs"); + let absolute_file_path = ctx.work_dir().join(&file_path); + std::fs::write( + &absolute_file_path, + "class C { string Value = \"SELECTION_SENTINEL\"; }", + ) + .expect("write selection file"); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - session - .send_and_wait( - MessageOptions::new("Summarize the selected code.").with_attachments(vec![ - Attachment::Selection { - file_path: file_path.clone(), - text: "string Value = \"SELECTION_SENTINEL\";".to_string(), - display_name: Some("selected-file.cs".to_string()), - selection: AttachmentSelectionRange { - start: AttachmentSelectionPosition { - line: 1, - character: 10, - }, - end: AttachmentSelectionPosition { - line: 1, - character: 45, + session + .send_and_wait( + MessageOptions::new("Summarize the selected code.").with_attachments(vec![ + Attachment::Selection { + file_path: file_path.clone(), + text: "string Value = \"SELECTION_SENTINEL\";".to_string(), + display_name: Some("selected-file.cs".to_string()), + selection: AttachmentSelectionRange { + start: AttachmentSelectionPosition { + line: 1, + character: 10, + }, + end: AttachmentSelectionPosition { + line: 1, + character: 45, + }, }, }, - }, - ]), - ) - .await - .expect("send"); + ]), + ) + .await + .expect("send"); - let user = latest_user_message(&session).await; - let attachment = user - .typed_data::() - .expect("user message data") - .attachments - .expect("attachments") - .into_iter() - .next() - .expect("attachment"); - assert_eq!( - attachment - .get("displayName") - .and_then(serde_json::Value::as_str), - Some("selected-file.cs") - ); - assert_eq!( - attachment - .get("filePath") - .and_then(serde_json::Value::as_str), - Some(file_path.to_string_lossy().as_ref()) - ); - assert_eq!( - attachment.get("text").and_then(serde_json::Value::as_str), - Some("string Value = \"SELECTION_SENTINEL\";") - ); + let user = latest_user_message(&session).await; + let attachment = user + .typed_data::() + .expect("user message data") + .attachments + .expect("attachments") + .into_iter() + .next() + .expect("attachment"); + assert_eq!( + attachment + .get("displayName") + .and_then(serde_json::Value::as_str), + Some("selected-file.cs") + ); + assert_eq!( + attachment + .get("filePath") + .and_then(serde_json::Value::as_str), + Some(file_path.to_string_lossy().as_ref()) + ); + assert_eq!( + attachment.get("text").and_then(serde_json::Value::as_str), + Some("string Value = \"SELECTION_SENTINEL\";") + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_send_with_github_reference_attachment() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "session", "should_send_with_github_reference_attachment", |ctx| { @@ -1394,101 +1471,114 @@ async fn should_send_with_github_reference_attachment() { #[tokio::test] async fn should_send_with_custom_requestheaders() { - with_e2e_context("session", "should_send_with_custom_requestheaders", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); - let mut headers = HashMap::new(); - headers.insert( - "x-copilot-sdk-test-header".to_string(), - "csharp-request-headers".to_string(), - ); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_send_with_custom_requestheaders", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); + let mut headers = HashMap::new(); + headers.insert( + "x-copilot-sdk-test-header".to_string(), + "csharp-request-headers".to_string(), + ); - session - .send_and_wait(MessageOptions::new("What is 1+1?").with_request_headers(headers)) - .await - .expect("send"); + session + .send_and_wait( + MessageOptions::new("What is 1+1?").with_request_headers(headers), + ) + .await + .expect("send"); - let exchanges = ctx.exchanges(); - assert!(!exchanges.is_empty(), "expected captured CAPI exchange"); - let request_headers = exchanges - .last() - .and_then(|exchange| exchange.get("requestHeaders")) - .and_then(serde_json::Value::as_object) - .expect("request headers"); - let header = request_headers - .iter() - .find(|(key, _)| key.eq_ignore_ascii_case("x-copilot-sdk-test-header")) - .and_then(|(_, value)| value.as_str()) - .expect("test header"); - assert!(header.contains("csharp-request-headers")); + let exchanges = ctx.exchanges(); + assert!(!exchanges.is_empty(), "expected captured CAPI exchange"); + let request_headers = exchanges + .last() + .and_then(|exchange| exchange.get("requestHeaders")) + .and_then(serde_json::Value::as_object) + .expect("request headers"); + let header = request_headers + .iter() + .find(|(key, _)| key.eq_ignore_ascii_case("x-copilot-sdk-test-header")) + .and_then(|(_, value)| value.as_str()) + .expect("test header"); + assert!(header.contains("csharp-request-headers")); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_send_with_mode_property() { - with_e2e_context("session", "should_send_with_mode_property", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let session = client - .create_session(ctx.approve_all_session_config()) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "session", + "should_send_with_mode_property", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let session = client + .create_session(ctx.approve_all_session_config()) + .await + .expect("create session"); - session - .client() - .call( - "session.send", - Some(json!({ - "sessionId": session.id().as_str(), - "prompt": "Say mode ok.", - "mode": "plan", - })), - ) - .await - .expect("send with agent mode"); - wait_for_event(session.subscribe(), "session.idle", |event| { - event.parsed_type() == SessionEventType::SessionIdle - }) - .await; + session + .client() + .call( + "session.send", + Some(json!({ + "sessionId": session.id().as_str(), + "prompt": "Say mode ok.", + "mode": "plan", + })), + ) + .await + .expect("send with agent mode"); + wait_for_event(session.subscribe(), "session.idle", |event| { + event.parsed_type() == SessionEventType::SessionIdle + }) + .await; - let user_message = session - .get_events() - .await - .expect("get messages") - .into_iter() - .rev() - .find(|event| event.parsed_type() == SessionEventType::UserMessage) - .expect("user.message"); - let data = user_message - .typed_data::() - .expect("user.message data"); - assert_eq!(data.content, "Say mode ok."); - assert!( - data.agent_mode.is_none(), - "runtime should accept but not echo per-message mode" - ); + let user_message = session + .get_events() + .await + .expect("get messages") + .into_iter() + .rev() + .find(|event| event.parsed_type() == SessionEventType::UserMessage) + .expect("user.message"); + let data = user_message + .typed_data::() + .expect("user.message data"); + assert_eq!(data.content, "Say mode ok."); + assert!( + data.agent_mode.is_none(), + "runtime should accept but not echo per-message mode" + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn should_create_session_with_custom_provider() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_session_with_custom_provider", |ctx| { @@ -1515,7 +1605,8 @@ async fn should_create_session_with_custom_provider() { #[tokio::test] async fn should_create_session_with_azure_provider() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_create_session_with_azure_provider", |ctx| { @@ -1545,7 +1636,8 @@ async fn should_create_session_with_azure_provider() { #[tokio::test] async fn should_resume_session_with_custom_provider() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session", "should_resume_session_with_custom_provider", |ctx| { @@ -1659,3 +1751,5 @@ fn secret_number_tool() -> Tool { })) .with_handler(Arc::new(SecretNumberTool)) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("session", 30); diff --git a/rust/tests/e2e/session_config.rs b/rust/tests/e2e/session_config.rs index dd498e376..c3f6b57ae 100644 --- a/rust/tests/e2e/session_config.rs +++ b/rust/tests/e2e/session_config.rs @@ -15,9 +15,10 @@ use http::{HeaderMap, HeaderValue}; use parking_lot::Mutex; use serde_json::{Value, json}; -use super::support::{ - DEFAULT_TEST_TOKEN, E2eContext, with_e2e_context, with_e2e_context_no_snapshot, -}; +use super::support::{DEFAULT_TEST_TOKEN, E2eContext, with_e2e_context_no_snapshot}; + +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("session_config", 4); const SYNTHETIC_TEXT: &str = "OK from the synthetic stream."; const CITATION_PROMPT: &str = "Summarize the attached PDF with citations enabled."; @@ -90,7 +91,8 @@ fn task_agent_types(exchange: &Value) -> Vec { #[tokio::test] async fn should_apply_session_limits_on_create() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_config", "should_apply_session_limits_on_create", |ctx| { @@ -123,7 +125,8 @@ async fn should_apply_session_limits_on_create() { #[tokio::test] async fn should_apply_session_limits_on_resume() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_config", "should_apply_session_limits_on_resume", |ctx| { @@ -169,7 +172,8 @@ async fn should_apply_session_limits_on_resume() { #[tokio::test] async fn should_apply_excluded_built_in_agents_on_create() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_config", "should_apply_excluded_built_in_agents_on_create", |ctx| { @@ -222,7 +226,8 @@ async fn should_apply_excluded_built_in_agents_on_create() { #[tokio::test] async fn should_apply_excluded_built_in_agents_on_resume() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_config", "should_apply_excluded_built_in_agents_on_resume", |ctx| { diff --git a/rust/tests/e2e/session_fs_sqlite.rs b/rust/tests/e2e/session_fs_sqlite.rs index 595a2c6b0..8ba712bb4 100644 --- a/rust/tests/e2e/session_fs_sqlite.rs +++ b/rust/tests/e2e/session_fs_sqlite.rs @@ -4,14 +4,14 @@ use std::sync::{Arc, Mutex}; use async_trait::async_trait; use github_copilot_sdk::session_fs::{FsError, FsErrorKind}; use github_copilot_sdk::{ - Client, DirEntry, DirEntryKind, FileInfo, SessionConfig, SessionFsCapabilities, - SessionFsConfig, SessionFsConventions, SessionFsProvider, SessionFsSqliteProvider, - SessionFsSqliteQueryResult, SessionFsSqliteQueryType, SessionFsSqliteTransactionError, - SessionFsSqliteTransactionStatement, + DirEntry, DirEntryKind, FileInfo, SessionConfig, SessionFsCapabilities, SessionFsConfig, + SessionFsConventions, SessionFsProvider, SessionFsSqliteProvider, SessionFsSqliteQueryResult, + SessionFsSqliteQueryType, SessionFsSqliteTransactionError, SessionFsSqliteTransactionStatement, }; use rusqlite::Connection; -use super::support::with_e2e_context; +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::new("session_fs_sqlite", sqlite_client_options, 2); #[derive(Debug)] struct SqliteCall { @@ -391,13 +391,12 @@ fn sqlite_session_fs_config() -> SessionFsConfig { .with_capabilities(SessionFsCapabilities::new().with_sqlite(true)) } -async fn start_sqlite_client(ctx: &super::support::E2eContext) -> Client { - Client::start( - ctx.client_options() - .with_session_fs(sqlite_session_fs_config()), - ) - .await - .expect("start sqlite client") +fn sqlite_client_options( + context: &super::support::E2eContext, +) -> github_copilot_sdk::ClientOptions { + context + .client_options() + .with_session_fs(sqlite_session_fs_config()) } fn sqlite_session_config( @@ -410,7 +409,8 @@ fn sqlite_session_config( #[tokio::test] async fn should_route_sql_queries_through_the_sessionfs_sqlite_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_fs_sqlite", "should_route_sql_queries_through_the_sessionfs_sqlite_handler", |ctx| { @@ -422,7 +422,7 @@ async fn should_route_sql_queries_through_the_sessionfs_sqlite_handler() { session_id, sqlite_calls.clone(), )); - let client = start_sqlite_client(ctx).await; + let client = ctx.start_client().await; let session = client .create_session( sqlite_session_config(ctx, provider).with_session_id(session_id), @@ -480,7 +480,8 @@ async fn should_route_sql_queries_through_the_sessionfs_sqlite_handler() { #[tokio::test] async fn should_allow_subagents_to_use_sql_tool_via_inherited_sessionfs() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_fs_sqlite", "should_allow_subagents_to_use_sql_tool_via_inherited_sessionfs", |ctx| { @@ -490,7 +491,7 @@ async fn should_allow_subagents_to_use_sql_tool_via_inherited_sessionfs() { let sqlite_calls = Arc::new(Mutex::new(Vec::new())); let provider = Arc::new(InMemorySqliteProvider::new(session_id, sqlite_calls.clone())); let provider_ref = provider.clone(); - let client = start_sqlite_client(ctx).await; + let client = ctx.start_client().await; let session = client .create_session( sqlite_session_config(ctx, provider).with_session_id(session_id), diff --git a/rust/tests/e2e/session_lifecycle.rs b/rust/tests/e2e/session_lifecycle.rs index 24938776f..545bb4988 100644 --- a/rust/tests/e2e/session_lifecycle.rs +++ b/rust/tests/e2e/session_lifecycle.rs @@ -2,12 +2,12 @@ use github_copilot_sdk::session_events::SessionEventType; use super::support::{ assistant_message_content, collect_until_idle, event_types, wait_for_condition, - with_e2e_context, }; #[tokio::test] async fn should_list_created_sessions_after_sending_a_message() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_lifecycle", "should_list_created_sessions_after_sending_a_message", |ctx| { @@ -59,7 +59,8 @@ async fn should_list_created_sessions_after_sending_a_message() { #[tokio::test] async fn should_delete_session_permanently() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_lifecycle", "should_delete_session_permanently", |ctx| { @@ -103,7 +104,8 @@ async fn should_delete_session_permanently() { #[tokio::test] async fn should_return_events_via_getmessages_after_conversation() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_lifecycle", "should_return_events_via_getmessages_after_conversation", |ctx| { @@ -136,7 +138,8 @@ async fn should_return_events_via_getmessages_after_conversation() { #[tokio::test] async fn should_support_multiple_concurrent_sessions() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_lifecycle", "should_support_multiple_concurrent_sessions", |ctx| { @@ -180,7 +183,8 @@ async fn should_support_multiple_concurrent_sessions() { #[tokio::test] async fn should_isolate_events_between_concurrent_sessions() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_lifecycle", "should_isolate_events_between_concurrent_sessions", |ctx| { @@ -255,3 +259,5 @@ async fn should_isolate_events_between_concurrent_sessions() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("session_lifecycle", 5); diff --git a/rust/tests/e2e/session_todos_changed.rs b/rust/tests/e2e/session_todos_changed.rs index ebace39b3..4b6245206 100644 --- a/rust/tests/e2e/session_todos_changed.rs +++ b/rust/tests/e2e/session_todos_changed.rs @@ -1,6 +1,6 @@ use github_copilot_sdk::session_events::SessionEventType; -use super::support::{wait_for_event, with_e2e_context}; +use super::support::wait_for_event; const PROMPT: &str = concat!( "Use the sql tool exactly once to execute all three of the following statements ", @@ -14,7 +14,8 @@ const PROMPT: &str = concat!( #[tokio::test] async fn fires_session_todos_changed_and_exposes_rows_and_dependencies() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "session_todos_changed", "fires_session_todos_changed_and_exposes_rows_and_dependencies", |ctx| { @@ -59,3 +60,5 @@ async fn fires_session_todos_changed_and_exposes_rows_and_dependencies() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("session_todos_changed", 1); diff --git a/rust/tests/e2e/skills.rs b/rust/tests/e2e/skills.rs index e0005ddf0..769b28b5f 100644 --- a/rust/tests/e2e/skills.rs +++ b/rust/tests/e2e/skills.rs @@ -2,13 +2,14 @@ use std::path::{Path, PathBuf}; use github_copilot_sdk::CustomAgentConfig; -use super::support::{assert_uuid_like, assistant_message_content, with_e2e_context}; +use super::support::{assert_uuid_like, assistant_message_content}; const SKILL_MARKER: &str = "PINEAPPLE_COCONUT_42"; #[tokio::test] async fn should_load_and_apply_skill_from_skilldirectories() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "skills", "should_load_and_apply_skill_from_skilldirectories", |ctx| { @@ -42,7 +43,8 @@ async fn should_load_and_apply_skill_from_skilldirectories() { #[tokio::test] async fn should_not_apply_skill_when_disabled_via_disabledskills() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "skills", "should_not_apply_skill_when_disabled_via_disabledskills", |ctx| { @@ -77,7 +79,8 @@ async fn should_not_apply_skill_when_disabled_via_disabledskills() { #[tokio::test] async fn should_allow_agent_with_skills_to_invoke_skill() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "skills", "should_allow_agent_with_skills_to_invoke_skill", |ctx| { @@ -118,7 +121,8 @@ async fn should_allow_agent_with_skills_to_invoke_skill() { #[tokio::test] async fn should_not_provide_skills_to_agent_without_skills_field() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "skills", "should_not_provide_skills_to_agent_without_skills_field", |ctx| { @@ -176,3 +180,4 @@ fn create_skill_dir(work_dir: &Path) -> PathBuf { .expect("write skill file"); skills_dir } +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("skills", 4); diff --git a/rust/tests/e2e/streaming_fidelity.rs b/rust/tests/e2e/streaming_fidelity.rs index 5a21a31d6..a48177174 100644 --- a/rust/tests/e2e/streaming_fidelity.rs +++ b/rust/tests/e2e/streaming_fidelity.rs @@ -7,11 +7,12 @@ use github_copilot_sdk::session_events::{ SessionStartData, }; -use super::support::{collect_until_idle, event_types, with_e2e_context}; +use super::support::{collect_until_idle, event_types}; #[tokio::test] async fn should_produce_delta_events_when_streaming_is_enabled() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "streaming_fidelity", "should_produce_delta_events_when_streaming_is_enabled", |ctx| { @@ -65,7 +66,7 @@ async fn should_produce_delta_events_when_streaming_is_enabled() { #[tokio::test] async fn should_not_produce_deltas_when_streaming_is_disabled() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "streaming_fidelity", "should_not_produce_deltas_when_streaming_is_disabled", |ctx| { @@ -107,7 +108,7 @@ async fn should_not_produce_deltas_when_streaming_is_disabled() { #[tokio::test] async fn should_produce_deltas_after_session_resume() { - with_e2e_context( + super::support::with_dedicated_e2e_context( "streaming_fidelity", "should_produce_deltas_after_session_resume", |ctx| { @@ -164,8 +165,7 @@ async fn should_produce_deltas_after_session_resume() { #[tokio::test] async fn should_not_produce_deltas_after_session_resume_with_streaming_disabled() { - with_e2e_context( - "streaming_fidelity", + super::support::with_dedicated_e2e_context("streaming_fidelity", "should_not_produce_deltas_after_session_resume_with_streaming_disabled", |ctx| { Box::pin(async move { @@ -227,7 +227,8 @@ async fn should_not_produce_deltas_after_session_resume_with_streaming_disabled( #[tokio::test] async fn should_emit_streaming_deltas_with_reasoning_effort_configured() { - with_e2e_context( + super::support::with_dedicated_group_e2e_context( + &E2E, "streaming_fidelity", "should_emit_streaming_deltas_with_reasoning_effort_configured", |ctx| { @@ -280,7 +281,8 @@ async fn should_emit_streaming_deltas_with_reasoning_effort_configured() { #[tokio::test] async fn should_emit_assistantmessage_start_before_deltas_with_matching_messageid() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "streaming_fidelity", "should_emit_assistantmessagestart_before_deltas_with_matching_messageid", |ctx| { @@ -362,3 +364,5 @@ fn assert_has_content_deltas(events: &[github_copilot_sdk::SessionEvent]) { assert!(!data.delta_content.is_empty()); } } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("streaming_fidelity", 3); diff --git a/rust/tests/e2e/support.rs b/rust/tests/e2e/support.rs index 4d9de5536..d65b049f9 100644 --- a/rust/tests/e2e/support.rs +++ b/rust/tests/e2e/support.rs @@ -1,13 +1,17 @@ use std::ffi::{OsStr, OsString}; use std::future::Future; use std::io::{BufRead, BufReader, Read, Write}; -use std::net::TcpStream; +use std::net::{TcpStream, ToSocketAddrs}; +use std::ops::Deref; +use std::panic::AssertUnwindSafe; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::process::{Child, Command, Stdio}; use std::sync::LazyLock; -use std::time::Duration; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::time::{Duration, Instant}; +use futures_util::FutureExt; use github_copilot_sdk::handler::ApproveAllHandler; use github_copilot_sdk::session::Session; use github_copilot_sdk::subscription::{EventSubscription, LifecycleSubscription}; @@ -16,15 +20,280 @@ use github_copilot_sdk::{ SessionId, SessionLifecycleEvent, Transport, }; use serde_json::json; -use tokio::sync::Semaphore; +use tokio::sync::{Mutex, Semaphore}; static E2E_CONCURRENCY: LazyLock = LazyLock::new(|| Semaphore::new(e2e_concurrency())); +static SHARED_E2E_RUNTIME: LazyLock = LazyLock::new(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .thread_name("rust-e2e-shared") + .build() + .expect("create shared E2E runtime") +}); +const SHARED_E2E_CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); pub const DEFAULT_TEST_TOKEN: &str = "rust-e2e-token"; type TestFuture<'a> = Pin + 'a>>; -pub async fn with_e2e_context(category: &str, snapshot_name: &str, test: F) +/// Fixed client options for one explicitly declared shared E2E group. +pub type SharedClientOptions = fn(&E2eContext) -> ClientOptions; + +/// A file- or group-scoped shared E2E runtime. +/// +/// This deliberately has no options-keyed registry: every Rust source group owns +/// its own static instance and selects its options at that declaration site. +pub struct SharedE2eGroup { + category: &'static str, + client_options: SharedClientOptions, + expected_invocations: usize, + completed_invocations: AtomicUsize, + state: Mutex>, +} + +struct SharedE2eState { + context: E2eContext, + client: Client, +} + +/// Test facade over a group's shared context and client. +/// +/// It dereferences to [`E2eContext`] for proxy and fixture helpers, while +/// [`Self::start_client`] returns a clone of the group's already-started client. +pub struct SharedE2eContext<'a> { + context: &'a mut E2eContext, + client: Client, +} + +/// A clone of a group's shared client. +/// +/// `stop` is deliberately a no-op: tests retain their existing local teardown +/// shape without shutting down the next test's runtime. The group stops the +/// actual client after its final expected invocation. Tests that verify +/// stopping or force-stopping a client stay on the dedicated helper. +#[derive(Clone)] +pub struct SharedE2eClient(Client); + +impl Deref for SharedE2eClient { + type Target = Client; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl SharedE2eClient { + pub async fn stop(&self) -> std::result::Result<(), github_copilot_sdk::StopErrors> { + Ok(()) + } +} + +impl Deref for SharedE2eContext<'_> { + type Target = E2eContext; + + fn deref(&self) -> &Self::Target { + self.context + } +} + +impl SharedE2eContext<'_> { + /// Clone the group client. Shared tests must not call `Client::stop`; the + /// group tears it down after its final expected test invocation. + pub async fn start_client(&self) -> SharedE2eClient { + SharedE2eClient(self.client.clone()) + } +} + +impl SharedE2eGroup { + pub const fn new( + category: &'static str, + client_options: SharedClientOptions, + expected_invocations: usize, + ) -> Self { + Self { + category, + client_options, + expected_invocations, + completed_invocations: AtomicUsize::new(0), + state: Mutex::const_new(None), + } + } + + pub const fn standard(category: &'static str, expected_invocations: usize) -> Self { + Self::new( + category, + standard_shared_client_options, + expected_invocations, + ) + } +} + +/// The standard stdio/default-transport options used by most shared groups. +pub fn standard_shared_client_options(context: &E2eContext) -> ClientOptions { + context.client_options() +} + +/// Run a test against an explicitly declared, file/group-scoped shared client. +/// +/// Calls using one group serialize, while different groups still use the suite +/// concurrency limit. Before and after every test, sessions are disconnected and +/// deleted, the work directory is emptied, and the proxy is reconfigured for the +/// test's snapshot so exchanges cannot bleed across tests. After the declared +/// number of invocations completes, the group's client and proxy are stopped. +pub async fn with_shared_e2e_context( + group: &'static SharedE2eGroup, + category: &str, + snapshot_name: &str, + test: F, +) where + F: for<'a> FnOnce(&'a mut SharedE2eContext<'a>) -> TestFuture<'a>, +{ + assert_eq!( + category, group.category, + "shared E2E group category must match the test's snapshots" + ); + let mut state = group.state.lock().await; + let _permit = E2E_CONCURRENCY + .acquire() + .await + .expect("E2E concurrency semaphore should stay open"); + let completed = group.completed_invocations.fetch_add(1, Ordering::Relaxed) + 1; + if state.is_none() { + let context = E2eContext::new(group.category, snapshot_name) + .await + .unwrap_or_else(|err| panic!("create shared E2E context: {err}")); + let _env_guard = InProcessEnvGuard::activate(&context); + let options = (group.client_options)(&context); + let mut startup = SHARED_E2E_RUNTIME.spawn(async move { + let client = Client::start(options).await?; + client.start_router_for_test(); + Ok::<_, github_copilot_sdk::Error>(client) + }); + let client = match tokio::time::timeout(default_test_timeout(), &mut startup).await { + Ok(result) => result + .expect("join shared E2E client startup") + .expect("start shared E2E client"), + Err(_) => { + startup.abort(); + let _ = tokio::time::timeout(SHARED_E2E_CLEANUP_TIMEOUT, startup).await; + panic!( + "timed out after {:?} starting shared E2E client", + default_test_timeout() + ); + } + }; + *state = Some(SharedE2eState { context, client }); + } + + let _env_guard = InProcessEnvGuard::activate( + &state + .as_ref() + .expect("shared E2E state initialized") + .context, + ); + let (result, cleanup_result) = { + let state = state.as_mut().expect("shared E2E state initialized"); + let result = match tokio::time::timeout( + SHARED_E2E_CLEANUP_TIMEOUT, + state.prepare_test(group.category, snapshot_name), + ) + .await + { + Ok(Ok(())) => Ok({ + let mut context = SharedE2eContext { + context: &mut state.context, + client: state.client.clone(), + }; + AssertUnwindSafe(tokio::time::timeout( + default_test_timeout(), + test(&mut context), + )) + .catch_unwind() + .await + }), + Ok(Err(error)) => Err(error), + Err(_) => Err(std::io::Error::other(format!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} preparing shared E2E test" + ))), + }; + let cleanup_result = match tokio::time::timeout( + SHARED_E2E_CLEANUP_TIMEOUT, + state.cleanup_after_test(), + ) + .await + { + Ok(result) => result, + Err(_) => { + state.client.force_stop(); + Err(std::io::Error::other(format!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} cleaning up shared E2E test" + ))) + } + }; + (result, cleanup_result) + }; + + let test_succeeded = matches!(&result, Ok(Ok(Ok(())))); + let skip_writing_cache = !test_succeeded || cleanup_result.is_err(); + let teardown_result = if !test_succeeded + || cleanup_result.is_err() + || is_filtered_test_run() + || completed == group.expected_invocations + { + state + .take() + .expect("shared E2E state initialized") + .shutdown_bounded(skip_writing_cache) + .await + } else { + Ok(()) + }; + + match result { + Ok(Ok(Ok(()))) => { + cleanup_result.unwrap_or_else(|error| panic!("clean up shared E2E test: {error}")); + teardown_result.unwrap_or_else(|error| panic!("tear down shared E2E group: {error}")); + } + Ok(Ok(Err(_))) => { + if let Err(error) = cleanup_result { + eprintln!("failed to clean up timed-out shared E2E test: {error}"); + } + if let Err(error) = teardown_result { + eprintln!("failed to tear down shared E2E group after timeout: {error}"); + } + panic!( + "timed out after {:?} running shared E2E test {}/{}", + default_test_timeout(), + group.category, + snapshot_name + ); + } + Ok(Err(payload)) => { + if let Err(error) = cleanup_result { + eprintln!("failed to clean up shared E2E test after panic: {error}"); + } + if let Err(error) = teardown_result { + eprintln!("failed to tear down shared E2E group after panic: {error}"); + } + std::panic::resume_unwind(payload); + } + Err(error) => { + if let Err(cleanup_error) = cleanup_result { + eprintln!( + "failed to clean up shared E2E test after setup failure: {cleanup_error}" + ); + } + if let Err(teardown_error) = teardown_result { + eprintln!( + "failed to tear down shared E2E group after setup failure: {teardown_error}" + ); + } + panic!("prepare shared E2E test: {error}"); + } + } +} + +pub async fn with_dedicated_e2e_context(category: &str, snapshot_name: &str, test: F) where F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, { @@ -56,11 +325,56 @@ where ); } -/// Like [`with_e2e_context`] but starts the CapiProxy without loading a +pub async fn with_dedicated_group_e2e_context( + _group: &'static SharedE2eGroup, + category: &str, + snapshot_name: &str, + test: F, +) where + F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, +{ + with_dedicated_e2e_context(category, snapshot_name, test).await; +} + +pub async fn skip_shared_e2e_inprocess(group: &'static SharedE2eGroup, reason: &str) -> bool { + if !skip_inprocess(reason) { + return false; + } + + let mut state = group.state.lock().await; + let _permit = E2E_CONCURRENCY + .acquire() + .await + .expect("E2E concurrency semaphore should stay open"); + let completed = group.completed_invocations.fetch_add(1, Ordering::Relaxed) + 1; + if completed == group.expected_invocations + && let Some(state) = state.take() + { + state + .shutdown_bounded(false) + .await + .unwrap_or_else(|error| panic!("tear down shared E2E group after skip: {error}")); + } + true +} + +/// Run a dedicated one-client E2E test. +/// +/// New tests should call [`with_dedicated_e2e_context`] to make the lifecycle +/// choice visible at the call site. This name remains for existing dedicated +/// tests while they are migrated group by group. +pub async fn with_e2e_context(category: &str, snapshot_name: &str, test: F) +where + F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, +{ + with_dedicated_e2e_context(category, snapshot_name, test).await; +} + +/// Like [`with_dedicated_e2e_context`] but starts the CapiProxy without loading a /// recorded snapshot. Used by the LLM inference callback tests, whose /// registered provider fabricates every model-layer response so no CAPI /// replay is needed — only the auth/user endpoints are served by the proxy. -pub async fn with_e2e_context_no_snapshot(test: F) +pub async fn with_dedicated_e2e_context_no_snapshot(test: F) where F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, { @@ -89,6 +403,15 @@ where ); } +/// Dedicated no-snapshot compatibility helper. See +/// [`with_dedicated_e2e_context_no_snapshot`]. +pub async fn with_e2e_context_no_snapshot(test: F) +where + F: for<'a> FnOnce(&'a mut E2eContext) -> TestFuture<'a>, +{ + with_dedicated_e2e_context_no_snapshot(test).await; +} + pub struct E2eContext { repo_root: PathBuf, cli_path: PathBuf, @@ -193,6 +516,7 @@ impl E2eContext { /// `node --embedded-host` argv itself and loads the sibling /// runtime cdylib), so a `.js` entrypoint is not split into node + /// prefix_args here. + #[cfg_attr(not(feature = "bundled-in-process"), allow(dead_code))] pub async fn start_inprocess_client(&self) -> Client { let options = ClientOptions::new().with_transport(Transport::InProcess); Client::start(options) @@ -350,6 +674,130 @@ impl E2eContext { } } +impl SharedE2eState { + async fn prepare_test(&mut self, category: &str, snapshot_name: &str) -> std::io::Result<()> { + self.cleanup_sessions().await?; + clear_directory_contents(self.context.work_dir())?; + self.context.configure(category, snapshot_name)?; + self.context.set_default_copilot_user(); + Ok(()) + } + + async fn cleanup_after_test(&mut self) -> std::io::Result<()> { + self.cleanup_sessions().await?; + clear_directory_contents(self.context.work_dir()) + } + + async fn cleanup_sessions(&self) -> std::io::Result<()> { + self.client + .cleanup_sessions_for_test() + .await + .map_err(|err| { + std::io::Error::other(format!("clean up shared E2E sessions failed: {err}")) + }) + } + + async fn shutdown_bounded(mut self, skip_writing_cache: bool) -> std::io::Result<()> { + let client_result = + match tokio::time::timeout(SHARED_E2E_CLEANUP_TIMEOUT, self.client.stop()).await { + Ok(result) => result.map_err(|err| { + std::io::Error::other(format!("stop shared E2E client failed: {err}")) + }), + Err(_) => { + self.client.force_stop(); + Err(std::io::Error::other(format!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} stopping shared E2E client" + ))) + } + }; + let proxy_result = self.context.cleanup(skip_writing_cache).await; + + match (client_result, proxy_result) { + (Ok(()), Ok(())) => Ok(()), + (Err(error), Ok(())) | (Ok(()), Err(error)) => Err(error), + (Err(client_error), Err(proxy_error)) => Err(std::io::Error::other(format!( + "{client_error}; stop shared E2E proxy failed: {proxy_error}" + ))), + } + } +} + +fn wait_for_child_exit(child: &mut Child) -> std::io::Result<()> { + let deadline = Instant::now() + SHARED_E2E_CLEANUP_TIMEOUT; + loop { + if child.try_wait()?.is_some() { + return Ok(()); + } + if Instant::now() >= deadline { + kill_and_wait_child(child); + return Err(std::io::Error::other(format!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} waiting for child process" + ))); + } + std::thread::sleep(Duration::from_millis(25)); + } +} + +fn kill_and_wait_child(child: &mut Child) { + if let Err(error) = child.kill() { + eprintln!("failed to kill E2E child process: {error}"); + } + let deadline = Instant::now() + SHARED_E2E_CLEANUP_TIMEOUT; + loop { + match child.try_wait() { + Ok(Some(_)) => return, + Ok(None) => {} + Err(error) => { + eprintln!("failed to inspect E2E child process after kill: {error}"); + return; + } + } + if Instant::now() >= deadline { + eprintln!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} waiting for killed E2E child process" + ); + return; + } + std::thread::sleep(Duration::from_millis(25)); + } +} + +fn connect_with_timeout(host: &str, port: u16) -> std::io::Result { + let mut last_error = None; + for address in (host, port).to_socket_addrs()? { + match TcpStream::connect_timeout(&address, SHARED_E2E_CLEANUP_TIMEOUT) { + Ok(stream) => { + stream.set_read_timeout(Some(SHARED_E2E_CLEANUP_TIMEOUT))?; + stream.set_write_timeout(Some(SHARED_E2E_CLEANUP_TIMEOUT))?; + return Ok(stream); + } + Err(error) => last_error = Some(error), + } + } + Err(last_error.unwrap_or_else(|| { + std::io::Error::other(format!("no socket addresses resolved for {host}:{port}")) + })) +} + +fn is_filtered_test_run() -> bool { + std::env::args().skip(1).any(|arg| { + !arg.starts_with('-') || matches!(arg.as_str(), "--ignored" | "--include-ignored") + }) +} + +fn clear_directory_contents(directory: &Path) -> std::io::Result<()> { + for entry in std::fs::read_dir(directory)? { + let entry = entry?; + let path = entry.path(); + if entry.file_type()?.is_dir() { + std::fs::remove_dir_all(path)?; + } else { + std::fs::remove_file(path)?; + } + } + Ok(()) +} + impl Drop for E2eContext { fn drop(&mut self) { if let Some(mut proxy) = self.proxy.take() { @@ -819,38 +1267,85 @@ impl CapiProxy { .spawn()?; let stdout = child.stdout.take().expect("proxy stdout"); - let reader = BufReader::new(stdout); + let (line_tx, line_rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + for line in BufReader::new(stdout).lines() { + let failed = line.is_err(); + if line_tx.send(line).is_err() || failed { + break; + } + } + }); let re = regex::Regex::new(r"Listening: (http://[^\s]+)\s+(\{.*\})$").unwrap(); - for line in reader.lines() { - let line = line?; + let deadline = Instant::now() + SHARED_E2E_CLEANUP_TIMEOUT; + while let Some(remaining) = deadline.checked_duration_since(Instant::now()) { + let line = match line_rx.recv_timeout(remaining) { + Ok(Ok(line)) => line, + Ok(Err(error)) => { + kill_and_wait_child(&mut child); + return Err(error); + } + Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => { + kill_and_wait_child(&mut child); + return Err(std::io::Error::other("proxy exited before startup")); + } + Err(std::sync::mpsc::RecvTimeoutError::Timeout) => break, + }; if let Some(captures) = re.captures(&line) { - let metadata: serde_json::Value = - serde_json::from_str(captures.get(2).unwrap().as_str())?; - let connect_proxy_url = metadata - .get("connectProxyUrl") - .and_then(|value| value.as_str()) - .expect("connectProxyUrl") - .to_string(); - let ca_file_path = metadata - .get("caFilePath") - .and_then(|value| value.as_str()) - .expect("caFilePath") - .to_string(); + let parsed = (|| { + let proxy_url = captures + .get(1) + .ok_or_else(|| { + std::io::Error::other("proxy startup line missing URL capture") + })? + .as_str() + .to_string(); + let metadata_text = captures.get(2).ok_or_else(|| { + std::io::Error::other("proxy startup line missing metadata capture") + })?; + let metadata: serde_json::Value = serde_json::from_str(metadata_text.as_str())?; + let connect_proxy_url = metadata + .get("connectProxyUrl") + .and_then(|value| value.as_str()) + .ok_or_else(|| { + std::io::Error::other("proxy startup metadata missing connectProxyUrl") + })? + .to_string(); + let ca_file_path = metadata + .get("caFilePath") + .and_then(|value| value.as_str()) + .ok_or_else(|| { + std::io::Error::other("proxy startup metadata missing caFilePath") + })? + .to_string(); + Ok::<_, std::io::Error>((proxy_url, connect_proxy_url, ca_file_path)) + })(); + let (proxy_url, connect_proxy_url, ca_file_path) = match parsed { + Ok(metadata) => metadata, + Err(error) => { + kill_and_wait_child(&mut child); + return Err(error); + } + }; return Ok(Self { child: Some(child), - proxy_url: captures.get(1).unwrap().as_str().to_string(), + proxy_url, connect_proxy_url, ca_file_path, }); } if line.contains("Listening: ") { + kill_and_wait_child(&mut child); return Err(std::io::Error::other(format!( "proxy startup line missing metadata: {line}" ))); } } - Err(std::io::Error::other("proxy exited before startup")) + kill_and_wait_child(&mut child); + Err(std::io::Error::other(format!( + "timed out after {SHARED_E2E_CLEANUP_TIMEOUT:?} waiting for proxy startup" + ))) } fn url(&self) -> &str { @@ -891,7 +1386,7 @@ impl CapiProxy { }; let result = self.post_json(path, ""); if let Some(mut child) = self.child.take() { - let _ = child.wait(); + wait_for_child_exit(&mut child)?; } result } @@ -943,7 +1438,7 @@ impl CapiProxy { fn request(&self, method: &str, path: &str, body: &str) -> std::io::Result { let (host, port) = parse_http_url(&self.proxy_url)?; - let mut stream = TcpStream::connect((host.as_str(), port))?; + let mut stream = connect_with_timeout(&host, port)?; write!( stream, "{method} {path} HTTP/1.1\r\nHost: {host}:{port}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", diff --git a/rust/tests/e2e/system_message_sections.rs b/rust/tests/e2e/system_message_sections.rs index e582d3846..f13336752 100644 --- a/rust/tests/e2e/system_message_sections.rs +++ b/rust/tests/e2e/system_message_sections.rs @@ -2,11 +2,12 @@ use std::collections::HashMap; use github_copilot_sdk::{SectionOverride, SystemMessageConfig}; -use super::support::{assistant_message_content, with_e2e_context}; +use super::support::assistant_message_content; #[tokio::test] async fn should_use_replaced_identity_section_in_response() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "system_message_sections", "should_use_replaced_identity_section_in_response", |ctx| { @@ -60,7 +61,8 @@ async fn should_use_replaced_identity_section_in_response() { #[tokio::test] async fn should_use_replaced_preamble_section_in_response() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "system_message_sections", "should_use_replaced_preamble_section_in_response", |ctx| { @@ -111,3 +113,5 @@ async fn should_use_replaced_preamble_section_in_response() { ) .await; } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("system_message_sections", 2); diff --git a/rust/tests/e2e/tool_results.rs b/rust/tests/e2e/tool_results.rs index e6e62643f..c46cacbf3 100644 --- a/rust/tests/e2e/tool_results.rs +++ b/rust/tests/e2e/tool_results.rs @@ -10,11 +10,12 @@ use github_copilot_sdk::{ use serde_json::json; use tokio::sync::mpsc; -use super::support::{assistant_message_content, collect_until_idle, with_e2e_context}; +use super::support::{assistant_message_content, collect_until_idle}; #[tokio::test] async fn should_handle_structured_toolresultobject_from_custom_tool() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "tool_results", "should_handle_structured_toolresultobject_from_custom_tool", |ctx| { @@ -41,7 +42,7 @@ async fn should_handle_structured_toolresultobject_from_custom_tool() { #[tokio::test] async fn should_handle_tool_result_with_failure_resulttype() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "tool_results", "should_handle_tool_result_with_failure_resulttype", |ctx| { @@ -69,7 +70,8 @@ async fn should_handle_tool_result_with_failure_resulttype() { #[tokio::test] async fn should_preserve_tooltelemetry_and_not_stringify_structured_results_for_llm() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "tool_results", "should_preserve_tooltelemetry_and_not_stringify_structured_results_for_llm", |ctx| { @@ -116,7 +118,7 @@ async fn should_preserve_tooltelemetry_and_not_stringify_structured_results_for_ #[tokio::test] async fn should_handle_tool_result_with_rejected_resulttype() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "tool_results", "should_handle_tool_result_with_rejected_resulttype", |ctx| { @@ -153,7 +155,7 @@ async fn should_handle_tool_result_with_rejected_resulttype() { #[tokio::test] async fn should_handle_tool_result_with_denied_resulttype() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "tool_results", "should_handle_tool_result_with_denied_resulttype", |ctx| { @@ -356,3 +358,5 @@ fn string_tool( "required": [parameter], })) } +static E2E: super::support::SharedE2eGroup = + super::support::SharedE2eGroup::standard("tool_results", 5); diff --git a/rust/tests/e2e/tools.rs b/rust/tests/e2e/tools.rs index 2c474bca1..586a31d3a 100644 --- a/rust/tests/e2e/tools.rs +++ b/rust/tests/e2e/tools.rs @@ -9,11 +9,11 @@ use github_copilot_sdk::{ use serde_json::json; use tokio::sync::{Mutex, mpsc}; -use super::support::{assistant_message_content, recv_with_timeout, with_e2e_context}; +use super::support::{assistant_message_content, recv_with_timeout}; #[tokio::test] async fn invokes_built_in_tools() { - with_e2e_context("tools", "invokes_built_in_tools", |ctx| { + super::support::with_shared_e2e_context(&E2E, "tools", "invokes_built_in_tools", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); std::fs::write( @@ -43,7 +43,7 @@ async fn invokes_built_in_tools() { #[tokio::test] async fn invokes_custom_tool() { - with_e2e_context("tools", "invokes_custom_tool", |ctx| { + super::support::with_shared_e2e_context(&E2E, "tools", "invokes_custom_tool", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -75,7 +75,7 @@ async fn invokes_custom_tool() { #[tokio::test] async fn low_level_tool_definition() { - with_e2e_context("tools", "low_level_tool_definition", |ctx| { + super::support::with_shared_e2e_context(&E2E, "tools", "low_level_tool_definition", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -124,7 +124,7 @@ async fn low_level_tool_definition() { #[tokio::test] async fn handles_tool_calling_errors() { - with_e2e_context("tools", "handles_tool_calling_errors", |ctx| { + super::support::with_shared_e2e_context(&E2E, "tools", "handles_tool_calling_errors", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -173,7 +173,7 @@ async fn handles_tool_calling_errors() { #[tokio::test] async fn can_receive_and_return_complex_types() { - with_e2e_context("tools", "can_receive_and_return_complex_types", |ctx| { + super::support::with_shared_e2e_context(&E2E, "tools", "can_receive_and_return_complex_types", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); let client = ctx.start_client().await; @@ -212,76 +212,89 @@ async fn can_receive_and_return_complex_types() { #[tokio::test] async fn overrides_built_in_tool_with_custom_tool() { - with_e2e_context("tools", "overrides_built_in_tool_with_custom_tool", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let __perm = Arc::new(ApproveAllHandler); - let tools = vec![custom_grep_tool()]; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(super::support::DEFAULT_TEST_TOKEN) - .with_permission_handler(__perm) - .with_tools(tools), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "tools", + "overrides_built_in_tool_with_custom_tool", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let __perm = Arc::new(ApproveAllHandler); + let tools = vec![custom_grep_tool()]; + let session = client + .create_session( + SessionConfig::default() + .with_github_token(super::support::DEFAULT_TEST_TOKEN) + .with_permission_handler(__perm) + .with_tools(tools), + ) + .await + .expect("create session"); - let answer = session - .send_and_wait("Use grep to search for the word 'hello'") - .await - .expect("send") - .expect("assistant message"); - assert!(assistant_message_content(&answer).contains("CUSTOM_GREP_RESULT")); + let answer = session + .send_and_wait("Use grep to search for the word 'hello'") + .await + .expect("send") + .expect("assistant message"); + assert!(assistant_message_content(&answer).contains("CUSTOM_GREP_RESULT")); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } #[tokio::test] async fn skippermission_sent_in_tool_definition() { - with_e2e_context("tools", "skippermission_sent_in_tool_definition", |ctx| { - Box::pin(async move { - ctx.set_default_copilot_user(); - let client = ctx.start_client().await; - let (permission_tx, mut permission_rx) = mpsc::unbounded_channel(); - let handler = Arc::new(RecordingPermissionHandler { - permission_tx, - decision: PermissionResult::reject(None), - }); - let __perm = handler; - let tools = vec![safe_lookup_tool()]; - let session = client - .create_session( - SessionConfig::default() - .with_github_token(super::support::DEFAULT_TEST_TOKEN) - .with_permission_handler(__perm) - .with_tools(tools), - ) - .await - .expect("create session"); + super::support::with_shared_e2e_context( + &E2E, + "tools", + "skippermission_sent_in_tool_definition", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let client = ctx.start_client().await; + let (permission_tx, mut permission_requests) = mpsc::unbounded_channel(); + let handler = Arc::new(RecordingPermissionHandler { + permission_tx, + decision: PermissionResult::reject(None), + }); + let __perm = handler; + let tools = vec![safe_lookup_tool()]; + let session = client + .create_session( + SessionConfig::default() + .with_github_token(super::support::DEFAULT_TEST_TOKEN) + .with_permission_handler(__perm) + .with_tools(tools), + ) + .await + .expect("create session"); - let answer = session - .send_and_wait("Use safe_lookup to look up 'test123'") - .await - .expect("send") - .expect("assistant message"); - assert!(assistant_message_content(&answer).contains("RESULT")); - assert!( - tokio::time::timeout(std::time::Duration::from_millis(100), permission_rx.recv()) + let answer = session + .send_and_wait("Use safe_lookup to look up 'test123'") + .await + .expect("send") + .expect("assistant message"); + assert!(assistant_message_content(&answer).contains("RESULT")); + assert!( + tokio::time::timeout( + std::time::Duration::from_millis(100), + permission_requests.recv() + ) .await .is_err(), - "skip_permission tool should not request permission" - ); + "skip_permission tool should not request permission" + ); - session.disconnect().await.expect("disconnect session"); - client.stop().await.expect("stop client"); - }) - }) + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) .await; } @@ -291,7 +304,8 @@ async fn can_return_binary_result() {} #[tokio::test] async fn invokes_custom_tool_with_permission_handler() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "tools", "invokes_custom_tool_with_permission_handler", |ctx| { @@ -334,7 +348,8 @@ async fn invokes_custom_tool_with_permission_handler() { #[tokio::test] async fn denies_custom_tool_when_permission_denied() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "tools", "denies_custom_tool_when_permission_denied", |ctx| { @@ -380,7 +395,7 @@ async fn denies_custom_tool_when_permission_denied() { #[tokio::test] async fn should_execute_multiple_custom_tools_in_parallel_single_turn() { - with_e2e_context( + super::support::with_shared_e2e_context(&E2E, "tools", "should_execute_multiple_custom_tools_in_parallel_single_turn", |ctx| { @@ -428,7 +443,8 @@ async fn should_execute_multiple_custom_tools_in_parallel_single_turn() { #[tokio::test] async fn should_respect_availabletools_and_excludedtools_combined() { - with_e2e_context( + super::support::with_shared_e2e_context( + &E2E, "tools", "should_respect_availabletools_and_excludedtools_combined", |ctx| { @@ -864,3 +880,4 @@ impl ToolHandler for DbQueryTool { )) } } +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("tools", 11); From 8acba83dd0cd5ba0a684b185a8f45748e76e7caa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:49:04 +0100 Subject: [PATCH 22/72] build(deps-dev): bump ip-address from 10.2.0 to 10.4.0 in /test/harness (#2245) Bumps [ip-address](https://github.com/beaugunderson/ip-address) from 10.2.0 to 10.4.0. - [Release notes](https://github.com/beaugunderson/ip-address/releases) - [Commits](https://github.com/beaugunderson/ip-address/compare/v10.2.0...v10.4.0) --- updated-dependencies: - dependency-name: ip-address dependency-version: 10.4.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/harness/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index da6e36ffb..da5ca35b7 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -1926,9 +1926,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", + "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", "dev": true, "license": "MIT", "engines": { From 1c92b2142b7f33efefff8f21f97aaa1f75451e80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:49:13 +0100 Subject: [PATCH 23/72] build(deps-dev): bump the npm_and_yarn group across 1 directory with 2 updates (#2244) Bumps the npm_and_yarn group with 2 updates in the /scripts/corrections directory: [postcss](https://github.com/postcss/postcss) and [undici](https://github.com/nodejs/undici). Updates `postcss` from 8.5.15 to 8.5.25 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.15...8.5.25) Updates `undici` from 6.27.0 to 6.28.0 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v6.27.0...v6.28.0) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.25 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: undici dependency-version: 6.28.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- scripts/corrections/package-lock.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/corrections/package-lock.json b/scripts/corrections/package-lock.json index 60559d62d..a975812af 100644 --- a/scripts/corrections/package-lock.json +++ b/scripts/corrections/package-lock.json @@ -1107,9 +1107,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -1164,9 +1164,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -1184,7 +1184,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -1334,9 +1334,9 @@ } }, "node_modules/undici": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", - "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", "dev": true, "license": "MIT", "engines": { From 0ec0a2b231b63de17e28e9f037518fb89b6be264 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:49:23 +0100 Subject: [PATCH 24/72] build(deps-dev): bump postcss from 8.5.15 to 8.5.25 in /nodejs (#2243) Bumps [postcss](https://github.com/postcss/postcss) from 8.5.15 to 8.5.25. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.15...8.5.25) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.25 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- nodejs/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 0829c3f70..b3c43e927 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -3235,9 +3235,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -3444,9 +3444,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -3464,7 +3464,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, From 9064f8de6589ba8e7bcb3c882e2fe871d1df852b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:49:30 +0100 Subject: [PATCH 25/72] build(deps-dev): bump fast-uri from 3.1.4 to 3.1.5 in /test/harness (#2242) Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.4...v3.1.5) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/harness/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index da5ca35b7..54c471dbf 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -1691,9 +1691,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ { From 69f2a4635c5761cab3d86d1e659370f52e333587 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 4 Aug 2026 16:24:30 +0100 Subject: [PATCH 26/72] docs: move SDK development guidance to local READMEs (#2253) * docs: move SDK development guidance to local READMEs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: simplify SDK development instructions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: address SDK development review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: document Node requirement for SDK tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: keep local test commands cross-platform Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CONTRIBUTING.md | 57 +++++++++--------------------------------------- dotnet/README.md | 19 ++++++++++++++++ go/README.md | 19 ++++++++++++++++ java/README.md | 2 +- nodejs/README.md | 15 +++++++++++++ python/README.md | 20 +++++++++++++++++ rust/README.md | 19 ++++++++++++++++ 7 files changed, 103 insertions(+), 48 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f60625ec1..5135e596d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,63 +33,26 @@ We are generally **not** looking for: - Additional documentation - **SDKs for other languages** — if you want to create a Copilot SDK for another language, we'd love to hear from you and may offer to link to your SDK from our repo. However we do not plan to add further language-specific SDKs to this repo in the short term, since we need to retain our maintenance capacity for moving forwards quickly with the existing language set. For other languages, please consider running your own external project. -## Prerequisites for Running and Testing Code +## Developing an SDK -This is a multi-language SDK repository. Install the tools for the SDK(s) you plan to work on: +Setup, build, and test instructions are maintained with each SDK: -### All SDKs - -1. The end-to-end tests across all languages use a shared test harness written in Node.js. Before running tests in any language, `cd test/harness && npm ci`. - -### Node.js/TypeScript SDK - -1. Install [Node.js](https://nodejs.org/) (v18+) -1. Install dependencies: `cd nodejs && npm ci` - -### Python SDK - -1. Install [Python 3.8+](https://www.python.org/downloads/) -1. Install [uv](https://github.com/astral-sh/uv) -1. Install dependencies: `cd python && uv pip install -e . --group dev` - -### Go SDK - -1. Install [Go 1.24+](https://go.dev/doc/install) -1. Install [golangci-lint](https://golangci-lint.run/welcome/install/#local-installation) -1. Install dependencies: `cd go && go mod download` - -### .NET SDK - -1. Install [.NET SDK 10+](https://dotnet.microsoft.com/download) -1. Install .NET dependencies: `cd dotnet && dotnet restore` +- [Node.js/TypeScript](nodejs/README.md#development) +- [Python](python/README.md#development) +- [Go](go/README.md#development) +- [.NET](dotnet/README.md#development) +- [Rust](rust/README.md#development) +- [Java](java/README.md#development-setup) ## Submitting a Pull Request 1. Fork and clone the repository -1. Install dependencies for the SDK(s) you're modifying (see above) -1. Make sure the tests pass on your machine (see commands below) -1. Make sure linter passes on your machine (see commands below) +1. Follow the development instructions for the SDK(s) you're modifying 1. Create a new branch: `git checkout -b my-branch-name` -1. Make your change, add tests, and make sure the tests and linter still pass +1. Make your change, add tests, and run the documented checks 1. Push to your fork and [submit a pull request][pr] 1. Pat yourself on the back and wait for your pull request to be reviewed and merged. -### Running Tests and Linters - -```bash -# Node.js -cd nodejs && npm test && npm run lint - -# Python -cd python && uv run pytest && uv run ruff check . - -# Go -cd go && go test ./... && golangci-lint run ./... - -# .NET -cd dotnet && dotnet test test/GitHub.Copilot.SDK.Test.csproj -``` - Here are a few things you can do that will increase the likelihood of your pull request being accepted: - Write tests. diff --git a/dotnet/README.md b/dotnet/README.md index 8f987893f..6efd6e094 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -1038,6 +1038,25 @@ catch (Exception ex) } ``` +## Development + +Development requires [.NET SDK 10+](https://dotnet.microsoft.com/download) and a supported [Node.js version](../nodejs/README.md#prerequisites). From the repository root: + +```bash +cd nodejs +npm ci +``` + +```bash +cd test/harness +npm ci +``` + +```bash +cd dotnet +dotnet test +``` + ## License MIT diff --git a/go/README.md b/go/README.md index 16053c1ab..d8588699c 100644 --- a/go/README.md +++ b/go/README.md @@ -978,6 +978,25 @@ Communicates with CLI via TCP socket. Useful for distributed scenarios. - `COPILOT_CLI_PATH` - Path to the Copilot CLI executable +## Development + +Tests require a supported [Node.js version](../nodejs/README.md#prerequisites). From the repository root: + +```bash +cd nodejs +npm ci +``` + +```bash +cd test/harness +npm ci +``` + +```bash +cd go +./test.sh +``` + ## License MIT diff --git a/java/README.md b/java/README.md index b772a2ce8..87a0ef7c5 100644 --- a/java/README.md +++ b/java/README.md @@ -422,7 +422,7 @@ The gate also applies to individual methods annotated with `@CopilotExperimental ### Development Setup -Requires JDK 25 or later for development. The following steps validate the artifact built with JDK 25 runs on both 25 and 17, preserving the MR-JAR behavior. +Requires JDK 25 or later and a supported [Node.js version](../nodejs/README.md#prerequisites) for development. The following steps validate the artifact built with JDK 25 runs on both 25 and 17, preserving the MR-JAR behavior. ```bash # Clone the repository diff --git a/nodejs/README.md b/nodejs/README.md index 20886e79a..eec674ce4 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -1109,6 +1109,21 @@ try { } ``` +## Development + +From the repository root: + +```bash +cd test/harness +npm ci +``` + +```bash +cd nodejs +npm ci +npm test +``` + ## License MIT diff --git a/python/README.md b/python/README.md index e540f9689..10630fb84 100644 --- a/python/README.md +++ b/python/README.md @@ -1142,3 +1142,23 @@ When `on_elicitation_request` is provided, the SDK automatically: - Reports the `elicitation` capability on the session - Dispatches `elicitation.requested` events to your handler - Auto-cancels if your handler throws an error (so the server doesn't hang) + +## Development + +Install [uv](https://docs.astral.sh/uv/) and a supported [Node.js version](../nodejs/README.md#prerequisites), then from the repository root: + +```bash +cd nodejs +npm ci +``` + +```bash +cd test/harness +npm ci +``` + +```bash +cd python +uv sync +uv run pytest +``` diff --git a/rust/README.md b/rust/README.md index 705166cdd..0bdaa400c 100644 --- a/rust/README.md +++ b/rust/README.md @@ -965,3 +965,22 @@ github-copilot-sdk = { version = "0.1", default-features = false } # Derive JSON Schema for tool parameters (adds to default bundled-cli). github-copilot-sdk = { version = "0.1", features = ["derive"] } ``` + +## Development + +Tests require a supported [Node.js version](../nodejs/README.md#prerequisites). From the repository root: + +```bash +cd nodejs +npm ci +``` + +```bash +cd test/harness +npm ci +``` + +```bash +cd rust +cargo test --features test-support +``` From ddf11bc6886d91d4da9173051116600642100b2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2026 16:25:02 +0100 Subject: [PATCH 27/72] build(deps-dev): bump postcss from 8.5.15 to 8.5.25 in /test/harness (#2252) Bumps [postcss](https://github.com/postcss/postcss) from 8.5.15 to 8.5.25. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.15...8.5.25) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.25 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/harness/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 54c471dbf..4702dbba6 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -2322,9 +2322,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -2508,9 +2508,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -2528,7 +2528,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, From 53f3a45620b593ad69e22be11731cbab7051334f Mon Sep 17 00:00:00 2001 From: examon <1676292+examon@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:24:44 +0200 Subject: [PATCH 28/72] docs: replace removed `session.idle.backgroundTasks` field with the current `aborted` field (#2232) The `session.idle` reference tables listed a `backgroundTasks` field of type `BackgroundTasks`. That field was removed from the payload and the docs were never updated; the type name no longer exists in the schema either. The payload's only member today is an optional `aborted` boolean, which was undocumented. Corrects the data-field row and the at-a-glance row in docs/features/streaming-events.md, plus the two Node.js quick-reference tables. Documentation only; no code, schema or generated file is affected. Co-authored-by: examon Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/features/streaming-events.md | 4 ++-- nodejs/docs/agent-author.md | 2 +- nodejs/docs/examples.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/streaming-events.md b/docs/features/streaming-events.md index d5201a168..10f111d9f 100644 --- a/docs/features/streaming-events.md +++ b/docs/features/streaming-events.md @@ -548,7 +548,7 @@ Ephemeral. The agent has finished all processing and is ready for the next messa | Data Field | Type | Required | Description | |------------|------|----------|-------------| -| `backgroundTasks` | `BackgroundTasks` | | Background agents/shells still running when the agent became idle | +| `aborted` | `boolean` | | True when the preceding turn was cancelled via abort signal | ### `session.error` @@ -958,7 +958,7 @@ This table lists key `data` payload fields. Common envelope fields are documente | `tool.execution_partial_result` | ✅ | Tool | `toolCallId`, `partialOutput` | | `tool.execution_progress` | ✅ | Tool | `toolCallId`, `progressMessage` | | `tool.execution_complete` | | Tool | `toolCallId`, `success`, `result?`, `error?` | -| `session.idle` | ✅ | Session | `backgroundTasks?` | +| `session.idle` | ✅ | Session | `aborted?` | | `session.error` | | Session | `errorType`, `message`, `statusCode?` | | `session.compaction_start` | | Session | *(empty)* | | `session.compaction_complete` | | Session | `success`, `preCompactionTokens?`, `summaryContent?` | diff --git a/nodejs/docs/agent-author.md b/nodejs/docs/agent-author.md index fa4bfb1ba..6b9366a7e 100644 --- a/nodejs/docs/agent-author.md +++ b/nodejs/docs/agent-author.md @@ -270,7 +270,7 @@ const unsub = session.on("tool.execution_complete", (event) => { | `tool.execution_start` | `toolCallId`, `toolName`, `arguments` | | `tool.execution_complete` | `toolCallId`, `success`, `result`, `error` | | `user.message` | `content`, `attachments`, `source` | -| `session.idle` | `backgroundTasks` | +| `session.idle` | `aborted` | | `session.error` | `errorType`, `message`, `stack` | | `permission.requested` | `requestId`, `permissionRequest.kind` | | `session.shutdown` | `shutdownType`, `totalPremiumRequests` | diff --git a/nodejs/docs/examples.md b/nodejs/docs/examples.md index a1c016cdf..63389c491 100644 --- a/nodejs/docs/examples.md +++ b/nodejs/docs/examples.md @@ -419,7 +419,7 @@ session.on("assistant.message", (event) => { | `tool.execution_start` | A tool is about to run | `toolCallId`, `toolName`, `arguments` | | `tool.execution_complete` | A tool finished running | `toolCallId`, `success`, `result`, `error` | | `user.message` | User sent a message | `content`, `attachments`, `source` | -| `session.idle` | Session finished processing a turn | `backgroundTasks` | +| `session.idle` | Session finished processing a turn | `aborted` | | `session.error` | An error occurred | `errorType`, `message`, `stack` | | `permission.requested` | Agent needs permission (shell, file write, etc.) | `requestId`, `permissionRequest.kind` | | `session.shutdown` | Session is ending | `shutdownType`, `totalPremiumRequests`, `codeChanges` | From dba8f83eca809affe8080b5dc1ee673182323996 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 4 Aug 2026 17:26:59 +0100 Subject: [PATCH 29/72] Parallelize Python and Windows .NET CI tests (#2251) * Run Python tests in parallel Use two pytest-xdist workers with file-level scheduling so module-scoped E2E clients and proxies remain together while independent modules run concurrently. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Shard Windows .NET tests across processes Run the slow Windows default-transport suite in two isolated jobs while preserving serial xUnit execution within each test host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/dotnet-sdk-tests.yml | 44 ++++++++++++++++++++++++-- .github/workflows/python-sdk-tests.yml | 4 ++- python/pyproject.toml | 1 + 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet-sdk-tests.yml b/.github/workflows/dotnet-sdk-tests.yml index 37cdc07f7..b5871897f 100644 --- a/.github/workflows/dotnet-sdk-tests.yml +++ b/.github/workflows/dotnet-sdk-tests.yml @@ -12,7 +12,7 @@ permissions: jobs: test: - name: ".NET SDK Tests (${{ matrix.os }}, ${{ matrix.transport }}, ${{ matrix.backend }})" + name: ".NET SDK Tests (${{ matrix.os }}, ${{ matrix.transport }}, ${{ matrix.backend }}, ${{ matrix.shard }})" if: github.event.repository.fork == false env: POWERSHELL_UPDATECHECK: Off @@ -24,22 +24,39 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] transport: ["default", "inprocess"] backend: [capi] + shard: [full] # TODO: Re-enable after fixing in-process sqlite file locking on shutdown on Windows. exclude: - os: windows-latest transport: "inprocess" + - os: windows-latest + transport: default + shard: full include: + # Keep xUnit serial within each process, but split the slow Windows + # default-transport suite across two isolated test hosts. + - os: windows-latest + transport: default + backend: capi + shard: "1" + - os: windows-latest + transport: default + backend: capi + shard: "2" - os: ubuntu-latest transport: inprocess backend: anthropic-messages + shard: full test-filter: "FullyQualifiedName~GitHub.Copilot.Test.E2E&E2EBackend!=SelfConfiguredBackend&E2EBackend!=CapiOnly" - os: ubuntu-latest transport: inprocess backend: openai-responses + shard: full test-filter: "FullyQualifiedName~GitHub.Copilot.Test.E2E&E2EBackend!=SelfConfiguredBackend&E2EBackend!=CapiOnly" - os: ubuntu-latest transport: inprocess backend: openai-completions + shard: full test-filter: "FullyQualifiedName~GitHub.Copilot.Test.E2E&E2EBackend!=SelfConfiguredBackend&E2EBackend!=CapiOnly" runs-on: ${{ matrix.os }} defaults: @@ -92,9 +109,30 @@ jobs: - name: Run .NET SDK tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + DOTNET_TEST_SHARD: ${{ matrix.shard }} run: | args=(--no-build -v n) - if [[ -n "$DOTNET_TEST_FILTER" ]]; then - args+=(--filter "$DOTNET_TEST_FILTER") + + filter="$DOTNET_TEST_FILTER" + if [[ "$DOTNET_TEST_SHARD" != "full" ]]; then + if [[ "$DOTNET_TEST_SHARD" == "1" ]]; then + initials=(A C D H I J K L N Q S U W Y) + shard_filter="FullyQualifiedName~GitHub.Copilot.Test.ConnectionToken" + else + initials=(B E F G M O P R T V X Z) + shard_filter="" + fi + + for namespace in E2E Unit; do + for initial in "${initials[@]}"; do + clause="FullyQualifiedName~GitHub.Copilot.Test.${namespace}.${initial}" + shard_filter="${shard_filter:+${shard_filter}|}${clause}" + done + done + filter="${filter:+(${filter})&}(${shard_filter})" + fi + + if [[ -n "$filter" ]]; then + args+=(--filter "$filter") fi dotnet test "${args[@]}" diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index 8c4d5806d..1ea973975 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -79,4 +79,6 @@ jobs: - name: Run Python SDK tests env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} - run: uv run pytest -v -s + # Keep each module's shared E2E client and proxy on one process while + # running independent modules concurrently in isolated workers. + run: uv run pytest -v -s -n 2 --dist=loadfile diff --git a/python/pyproject.toml b/python/pyproject.toml index 7e6274d9c..e96c587a6 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -46,6 +46,7 @@ dev = [ "pytest>=7.0.0", "pytest-asyncio>=0.21.0", "pytest-timeout>=2.0.0", + "pytest-xdist>=3.6.0", "websockets>=12.0", "opentelemetry-sdk>=1.0.0", ] From 70bb9a101dce5da9e2c2806e878aa75662c1d04b Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 4 Aug 2026 19:08:26 +0200 Subject: [PATCH 30/72] Fix active Node and Rust replay E2E flakes (#2186) * Raise Node E2E timeout budget on Windows Use a 60-second Vitest test and hook budget on Windows runners while preserving the existing 30-second budget elsewhere. This covers both default and in-process transports without weakening individual assertions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 51fbb14d-022f-4acb-bdce-1753cba236f9 * Split Windows .NET target frameworks across runners Run net8.0 and net472 in independent Windows matrix jobs instead of concurrently in one testhost job. This isolates each CLI and replay-proxy fixture graph without increasing the workflow's critical-path test duration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 51fbb14d-022f-4acb-bdce-1753cba236f9 * Give Rust metadata E2E its own replay fixture Rename the repurposed metadata test and route it to a snapshot that matches its RUST_CONTEXT_INFO prompt. Preserve the existing compaction snapshot used by Python instead of sharing an incompatible conversation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 51fbb14d-022f-4acb-bdce-1753cba236f9 * Keep target frameworks in existing test shards The framework split reduced the measured Windows critical path by only 0.1 minutes while doubling the number of Windows jobs. Preserve the class-level process sharding that reduced the critical path by roughly seven minutes instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 51fbb14d-022f-4acb-bdce-1753cba236f9 --- .github/workflows/dotnet-sdk-tests.yml | 4 +++- nodejs/vitest.config.ts | 6 ++++-- rust/tests/e2e/rpc_session_state.rs | 4 ++-- .../should_report_processing_and_context_metadata.yaml | 10 ++++++++++ 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 test/snapshots/rpc_session_state/should_report_processing_and_context_metadata.yaml diff --git a/.github/workflows/dotnet-sdk-tests.yml b/.github/workflows/dotnet-sdk-tests.yml index b5871897f..fa2e2dc75 100644 --- a/.github/workflows/dotnet-sdk-tests.yml +++ b/.github/workflows/dotnet-sdk-tests.yml @@ -34,7 +34,9 @@ jobs: shard: full include: # Keep xUnit serial within each process, but split the slow Windows - # default-transport suite across two isolated test hosts. + # default-transport suite across two isolated test hosts. Keep both + # target frameworks in each shard: separate framework jobs did not + # shorten the critical path and doubled the Windows job count. - os: windows-latest transport: default backend: capi diff --git a/nodejs/vitest.config.ts b/nodejs/vitest.config.ts index 03f6c779e..bb07cb017 100644 --- a/nodejs/vitest.config.ts +++ b/nodejs/vitest.config.ts @@ -1,11 +1,13 @@ import { defineConfig } from "vitest/config"; +const integrationTestTimeout = process.platform === "win32" ? 60000 : 30000; + export default defineConfig({ test: { globals: true, environment: "node", - testTimeout: 30000, // 30 seconds for integration tests - hookTimeout: 30000, + testTimeout: integrationTestTimeout, + hookTimeout: integrationTestTimeout, teardownTimeout: 10000, isolate: true, // Run each test file in isolation pool: "forks", // Use process forking for better isolation diff --git a/rust/tests/e2e/rpc_session_state.rs b/rust/tests/e2e/rpc_session_state.rs index 01377780e..c705d231c 100644 --- a/rust/tests/e2e/rpc_session_state.rs +++ b/rust/tests/e2e/rpc_session_state.rs @@ -1098,11 +1098,11 @@ async fn should_report_implemented_errors_for_unsupported_session_rpc_paths() { } #[tokio::test] -async fn should_compact_session_history_after_messages() { +async fn should_report_processing_and_context_metadata() { super::support::with_shared_e2e_context( &E2E, "rpc_session_state", - "should_compact_session_history_after_messages", + "should_report_processing_and_context_metadata", |ctx| { Box::pin(async move { ctx.set_default_copilot_user(); diff --git a/test/snapshots/rpc_session_state/should_report_processing_and_context_metadata.yaml b/test/snapshots/rpc_session_state/should_report_processing_and_context_metadata.yaml new file mode 100644 index 000000000..6760888d7 --- /dev/null +++ b/test/snapshots/rpc_session_state/should_report_processing_and_context_metadata.yaml @@ -0,0 +1,10 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: "Reply with exactly: RUST_CONTEXT_INFO" + - role: assistant + content: RUST_CONTEXT_INFO From 2b050d76f868aecf87da40e176173d03baa7f7f4 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 4 Aug 2026 18:20:55 +0100 Subject: [PATCH 31/72] Add userPromptTransformed hook to all SDKs (#2254) * Add user prompt transformed hook to all SDKs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address user prompt hook review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Complete hook registration examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Synchronize transformed hook test inputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/README.md | 1 + docs/features/hooks.md | 19 +-- docs/hooks/README.md | 1 + docs/hooks/hooks-overview.md | 2 + docs/hooks/user-prompt-transformed.md | 129 ++++++++++++++++++ dotnet/src/Client.cs | 2 + dotnet/src/Session.cs | 7 + dotnet/src/Types.cs | 54 ++++++++ .../E2E/HookLifecycleAndOutputE2ETests.cs | 36 ++++- go/client.go | 2 + go/internal/e2e/hooks_extended_e2e_test.go | 58 +++++++- go/session.go | 10 ++ go/types.go | 59 ++++++-- go/types_test.go | 12 ++ .../com/github/copilot/CopilotSession.java | 12 ++ .../com/github/copilot/rpc/SessionHooks.java | 28 +++- .../rpc/UserPromptTransformedHandler.java | 28 ++++ .../rpc/UserPromptTransformedHookInput.java | 29 ++++ .../rpc/UserPromptTransformedHookOutput.java | 20 +++ .../java/com/github/copilot/HooksTest.java | 32 +++++ .../github/copilot/SessionHandlerTest.java | 21 +++ nodejs/src/index.ts | 3 + nodejs/src/session.ts | 1 + nodejs/src/types.ts | 32 +++++ nodejs/test/e2e/hooks_extended.e2e.test.ts | 31 +++++ python/copilot/__init__.py | 6 + python/copilot/session.py | 24 ++++ python/e2e/test_hooks_extended_e2e.py | 29 +++- rust/README.md | 2 +- rust/src/hooks.rs | 90 ++++++++++++ rust/tests/e2e/hooks_extended.rs | 68 ++++++++- ...ed_hook_and_modify_transformed_prompt.yaml | 10 ++ 32 files changed, 832 insertions(+), 26 deletions(-) create mode 100644 docs/hooks/user-prompt-transformed.md create mode 100644 java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java create mode 100644 java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java create mode 100644 java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java create mode 100644 test/snapshots/hooks_extended/should_invoke_userprompttransformed_hook_and_modify_transformed_prompt.yaml diff --git a/docs/README.md b/docs/README.md index ebb287d98..3be019f14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -63,6 +63,7 @@ Detailed API reference for each session hook. * [Pre-Tool Use](./hooks/pre-tool-use.md): approve, deny, or modify tool calls * [Post-Tool Use](./hooks/post-tool-use.md): transform tool results * [User Prompt Submitted](./hooks/user-prompt-submitted.md): modify or filter user messages +* [User Prompt Transformed](./hooks/user-prompt-transformed.md): inspect or replace model-facing prompts * [Session Lifecycle](./hooks/session-lifecycle.md): session start and end * [Error Handling](./hooks/error-handling.md): custom error handling diff --git a/docs/features/hooks.md b/docs/features/hooks.md index feee55546..6a7833990 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -9,20 +9,22 @@ A hook is a callback you register once when creating a session. The SDK invokes ```mermaid flowchart LR A[Session starts] -->|onSessionStart| B[User sends prompt] - B -->|onUserPromptSubmitted| C[Agent picks a tool] - C -->|onPreToolUse| D[Tool executes] - D -->|onPostToolUse| E{More work?} - E -->|yes| C - E -->|no| F[Session ends] - F -->|onSessionEnd| G((Done)) - C -.->|error| H[onErrorOccurred] - D -.->|error| H + B -->|onUserPromptSubmitted| C[Runtime transforms prompt] + C -->|onUserPromptTransformed| D[Agent picks a tool] + D -->|onPreToolUse| E[Tool executes] + E -->|onPostToolUse| F{More work?} + F -->|yes| D + F -->|no| G[Session ends] + G -->|onSessionEnd| H((Done)) + D -.->|error| I[onErrorOccurred] + E -.->|error| I ``` | Hook | When it fires | What you can do | | ------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------ | | [`onSessionStart`](../hooks/session-lifecycle.md#session-start) | Session begins (new or resumed) | Inject context, load preferences | | [`onUserPromptSubmitted`](../hooks/user-prompt-submitted.md) | User sends a message | Rewrite prompts, add context, filter input | +| [`onUserPromptTransformed`](../hooks/user-prompt-transformed.md) | Runtime builds the model prompt | Inspect or replace model-facing content | | [`onPreToolUse`](../hooks/pre-tool-use.md) | Before a tool executes | Allow / deny / modify the call | | [`onPostToolUse`](../hooks/post-tool-use.md) | After a tool returns (success only) | Transform results, redact secrets, audit | | [`onPostToolUseFailure`](../hooks/post-tool-use.md#failure-variant) | After a tool returns a failure | Inject retry guidance, log failures | @@ -1055,6 +1057,7 @@ For full type definitions, input/output field tables, and additional examples fo * [Pre-Tool Use](../hooks/pre-tool-use.md) * [Post-Tool Use](../hooks/post-tool-use.md) * [User Prompt Submitted](../hooks/user-prompt-submitted.md) +* [User Prompt Transformed](../hooks/user-prompt-transformed.md) * [Session Lifecycle](../hooks/session-lifecycle.md) * [Error Handling](../hooks/error-handling.md) diff --git a/docs/hooks/README.md b/docs/hooks/README.md index 517be9614..a6c7e1aa6 100644 --- a/docs/hooks/README.md +++ b/docs/hooks/README.md @@ -6,5 +6,6 @@ Detailed API reference for each session hook in the GitHub Copilot SDK. * [Pre-tool use](./pre-tool-use.md): approve, deny, or modify tool calls * [Post-tool use](./post-tool-use.md): transform tool results * [User prompt submitted](./user-prompt-submitted.md): modify or filter user messages +* [User prompt transformed](./user-prompt-transformed.md): inspect or replace model-facing prompts * [Session lifecycle](./session-lifecycle.md): session start and end * [Error handling](./error-handling.md): custom error handling diff --git a/docs/hooks/hooks-overview.md b/docs/hooks/hooks-overview.md index 6de4c3e72..8d5583e99 100644 --- a/docs/hooks/hooks-overview.md +++ b/docs/hooks/hooks-overview.md @@ -16,6 +16,7 @@ Hooks allow you to intercept and customize the behavior of Copilot sessions at k | [`onPostToolUse`](./post-tool-use.md) | After a tool executes (success only) | Result transformation, logging | | [`onPostToolUseFailure`](./post-tool-use.md#failure-variant) | After a tool execution whose result was a failure | Inject retry guidance, log failures | | [`onUserPromptSubmitted`](./user-prompt-submitted.md) | When user sends a message | Prompt modification, filtering | +| [`onUserPromptTransformed`](./user-prompt-transformed.md) | After runtime prompt transformation | Inspect or replace model-facing content | | [`onSessionStart`](./session-lifecycle.md#session-start) | Session begins | Add context, configure session | | [`onSessionEnd`](./session-lifecycle.md#session-end) | Session ends | Cleanup, analytics | | [`onErrorOccurred`](./error-handling.md) | Error happens | Custom error handling | @@ -263,6 +264,7 @@ const session = await client.createSession({ * **[Pre-Tool Use Hook](./pre-tool-use.md)** - Control tool execution permissions * **[Post-Tool Use Hook](./post-tool-use.md)** - Transform tool results * **[User Prompt Submitted Hook](./user-prompt-submitted.md)** - Modify user prompts +* **[User Prompt Transformed Hook](./user-prompt-transformed.md)** - Replace model-facing prompts * **[Session Lifecycle Hooks](./session-lifecycle.md)** - Session start and end * **[Agent Stop Hook](./session-lifecycle.md#agent-stop)** - Validate completion before the agent stops * **[Error Handling Hook](./error-handling.md)** - Custom error handling diff --git a/docs/hooks/user-prompt-transformed.md b/docs/hooks/user-prompt-transformed.md new file mode 100644 index 000000000..f7791d78b --- /dev/null +++ b/docs/hooks/user-prompt-transformed.md @@ -0,0 +1,129 @@ +# User prompt transformed hook + +The `userPromptTransformed` hook runs after the runtime adds generated context to a submitted prompt, but before the resulting content is persisted to session history or sent to the model. + +Use it when you need to inspect or replace the exact model-facing prompt. The `prompt` input contains the user prompt after any `userPromptSubmitted` hooks have run, while `transformedPrompt` also contains runtime-generated context such as ``. + +## Input and output + +| Input field | Type | Description | +| --- | --- | --- | +| `sessionId` | string | Runtime session ID | +| `timestamp` | date/time | Time the hook was invoked | +| `cwd` / `workingDirectory` | string | Current working directory | +| `prompt` | string | Prompt after `userPromptSubmitted` hooks | +| `transformedPrompt` | string | Model-facing prompt after runtime transformations | + +Return no value to leave the transformed prompt unchanged. Return `modifiedTransformedPrompt` to replace the content that is stored in session history and sent to the model. + +## Examples + +

+TypeScript + + +```typescript +const session = await client.createSession({ + hooks: { + onUserPromptTransformed: async (input) => ({ + modifiedTransformedPrompt: redact(input.transformedPrompt), + }), + }, +}); +``` + +
+ +
+Python + + +```python +session = await client.create_session( + hooks={ + "on_user_prompt_transformed": lambda input_data, invocation: { + "modifiedTransformedPrompt": redact(input_data["transformedPrompt"]) + } + } +) +``` + +
+ +
+Go + + +```go +session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + Hooks: &copilot.SessionHooks{ + OnUserPromptTransformed: func(input copilot.UserPromptTransformedHookInput, invocation copilot.HookInvocation) (*copilot.UserPromptTransformedHookOutput, error) { + return &copilot.UserPromptTransformedHookOutput{ + ModifiedTransformedPrompt: copilot.String(redact(input.TransformedPrompt)), + }, nil + }, + }, +}) +``` + +
+ +
+.NET + + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Hooks = new SessionHooks + { + OnUserPromptTransformed = (input, invocation) => + Task.FromResult(new() + { + ModifiedTransformedPrompt = Redact(input.TransformedPrompt), + }), + }, +}); +``` + +
+ +
+Java + + +```java +var hooks = new SessionHooks().setOnUserPromptTransformed((input, invocation) -> + CompletableFuture.completedFuture( + new UserPromptTransformedHookOutput(redact(input.transformedPrompt())))); + +var session = client.createSession(new SessionConfig().setHooks(hooks)).get(); +``` + +
+ +
+Rust + +```rust +#[async_trait] +impl SessionHooks for MyHooks { + async fn on_user_prompt_transformed( + &self, + input: UserPromptTransformedInput, + _ctx: HookContext, + ) -> Option { + Some(UserPromptTransformedOutput { + modified_transformed_prompt: Some(redact(&input.transformed_prompt)), + }) + } +} + +let session = client + .create_session(SessionConfig::default().with_hooks(Arc::new(MyHooks))) + .await?; +``` + +
+ +The replacement is persisted as the user message content, so resumed sessions replay the modified content unchanged. diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index b1199dac8..4e271598e 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1097,6 +1097,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.Hooks.OnPostToolUse != null || config.Hooks.OnPostToolUseFailure != null || config.Hooks.OnUserPromptSubmitted != null || + config.Hooks.OnUserPromptTransformed != null || config.Hooks.OnSessionStart != null || config.Hooks.OnSessionEnd != null || config.Hooks.OnErrorOccurred != null || @@ -1327,6 +1328,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.Hooks.OnPostToolUse != null || config.Hooks.OnPostToolUseFailure != null || config.Hooks.OnUserPromptSubmitted != null || + config.Hooks.OnUserPromptTransformed != null || config.Hooks.OnSessionStart != null || config.Hooks.OnSessionEnd != null || config.Hooks.OnErrorOccurred != null || diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index e3bd56af9..7c34ded16 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -1613,6 +1613,11 @@ internal void RegisterHooks(SessionHooks hooks) JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.UserPromptSubmittedHookInput)!, invocation) : null, + "userPromptTransformed" => hooks.OnUserPromptTransformed != null + ? await hooks.OnUserPromptTransformed( + JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.UserPromptTransformedHookInput)!, + invocation) + : null, "sessionStart" => hooks.OnSessionStart != null ? await hooks.OnSessionStart( JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.SessionStartHookInput)!, @@ -2033,5 +2038,7 @@ internal void ThrowIfDisposed() [JsonSerializable(typeof(Attachment))] [JsonSerializable(typeof(UserPromptSubmittedHookInput))] [JsonSerializable(typeof(UserPromptSubmittedHookOutput))] + [JsonSerializable(typeof(UserPromptTransformedHookInput))] + [JsonSerializable(typeof(UserPromptTransformedHookOutput))] internal partial class SessionJsonContext : JsonSerializerContext; } diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 1f64b6f9d..a6a1a8c4e 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -1656,6 +1656,55 @@ public sealed class UserPromptSubmittedHookOutput public bool? SuppressOutput { get; set; } } +/// +/// Input for a user-prompt-transformed hook. +/// +public sealed class UserPromptTransformedHookInput +{ + /// + /// The runtime session ID of the session that triggered the hook. + /// + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// + /// Unix timestamp in milliseconds when the prompt was transformed. + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(UnixMillisecondsDateTimeOffsetConverter))] + public DateTimeOffset Timestamp { get; set; } + + /// + /// Current working directory of the session. + /// + [JsonPropertyName("cwd")] + public string WorkingDirectory { get; set; } = string.Empty; + + /// + /// The user prompt after any user-prompt-submitted hooks have run. + /// + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// + /// The model-facing prompt after runtime transformations. + /// + [JsonPropertyName("transformedPrompt")] + public string TransformedPrompt { get; set; } = string.Empty; +} + +/// +/// Output for a user-prompt-transformed hook. +/// +public sealed class UserPromptTransformedHookOutput +{ + /// + /// Replacement model-facing prompt to persist and send to the model. + /// + [JsonPropertyName("modifiedTransformedPrompt")] + public string? ModifiedTransformedPrompt { get; set; } +} + /// /// Input for a session-start hook. /// @@ -1967,6 +2016,11 @@ public sealed class SessionHooks ///
public Func>? OnUserPromptSubmitted { get; set; } + /// + /// Handler called after the runtime transforms a submitted prompt and before it is stored. + /// + public Func>? OnUserPromptTransformed { get; set; } + /// /// Handler called when a session starts. /// diff --git a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs index cd94a2ebd..decdb3190 100644 --- a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs +++ b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs @@ -11,8 +11,9 @@ namespace GitHub.Copilot.Test.E2E; /// /// E2E coverage for every handler exposed on : /// OnPreToolUse, OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, -/// OnSessionStart, OnSessionEnd, OnErrorOccurred, OnAgentStop. Output-shape behavior -/// (modifiedPrompt / additionalContext / errorHandling / modifiedArgs / +/// OnUserPromptTransformed, OnSessionStart, OnSessionEnd, OnErrorOccurred, +/// OnAgentStop. Output-shape behavior (modifiedPrompt / modifiedTransformedPrompt / +/// additionalContext / errorHandling / modifiedArgs / /// modifiedResult / sessionSummary) is asserted alongside hook invocation. If a /// new handler is added to SessionHooks, add a corresponding test here. /// @@ -163,6 +164,37 @@ public async Task Should_Invoke_UserPromptSubmitted_Hook_And_Modify_Prompt() Assert.Contains("HOOKED_PROMPT", response?.Data.Content ?? string.Empty); } + [Fact] + public async Task Should_Invoke_UserPromptTransformed_Hook_And_Modify_Transformed_Prompt() + { + var inputs = new List(); + var session = await CreateSessionAsync(new SessionConfig + { + Hooks = new SessionHooks + { + OnUserPromptTransformed = (input, invocation) => + { + inputs.Add(input); + Assert.False(string.IsNullOrWhiteSpace(invocation.SessionId)); + return Task.FromResult(new UserPromptTransformedHookOutput + { + ModifiedTransformedPrompt = "Reply with exactly: HOOKED_TRANSFORMED_PROMPT", + }); + }, + }, + }); + + var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Answer the request above." }); + + Assert.NotEmpty(inputs); + Assert.Contains("Answer the request above.", inputs[0].Prompt); + Assert.Contains("Answer the request above.", inputs[0].TransformedPrompt); + Assert.Contains("", inputs[0].TransformedPrompt); + Assert.True(inputs[0].Timestamp > DateTimeOffset.UnixEpoch); + Assert.False(string.IsNullOrEmpty(inputs[0].WorkingDirectory)); + Assert.Contains("HOOKED_TRANSFORMED_PROMPT", response?.Data.Content ?? string.Empty); + } + [Fact] public async Task Should_Invoke_SessionStart_Hook() { diff --git a/go/client.go b/go/client.go index f867b7db2..eb9513030 100644 --- a/go/client.go +++ b/go/client.go @@ -871,6 +871,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses config.Hooks.OnPostToolUse != nil || config.Hooks.OnPostToolUseFailure != nil || config.Hooks.OnUserPromptSubmitted != nil || + config.Hooks.OnUserPromptTransformed != nil || config.Hooks.OnSessionStart != nil || config.Hooks.OnSessionEnd != nil || config.Hooks.OnErrorOccurred != nil || @@ -1159,6 +1160,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, config.Hooks.OnPostToolUse != nil || config.Hooks.OnPostToolUseFailure != nil || config.Hooks.OnUserPromptSubmitted != nil || + config.Hooks.OnUserPromptTransformed != nil || config.Hooks.OnSessionStart != nil || config.Hooks.OnSessionEnd != nil || config.Hooks.OnErrorOccurred != nil || diff --git a/go/internal/e2e/hooks_extended_e2e_test.go b/go/internal/e2e/hooks_extended_e2e_test.go index 1afb05a13..5cbba3856 100644 --- a/go/internal/e2e/hooks_extended_e2e_test.go +++ b/go/internal/e2e/hooks_extended_e2e_test.go @@ -14,8 +14,9 @@ import ( // Mirrors dotnet/test/HookLifecycleAndOutputTests.cs (snapshot category "hooks_extended"). // // Covers each handler exposed on copilot.SessionHooks: OnPreToolUse, -// OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, OnSessionStart, -// OnSessionEnd, OnErrorOccurred, OnAgentStop. Output-shape behavior (modifiedPrompt / +// OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, +// OnUserPromptTransformed, OnSessionStart, OnSessionEnd, OnErrorOccurred, +// OnAgentStop. Output-shape behavior (modifiedPrompt / modifiedTransformedPrompt / // additionalContext / errorHandling / modifiedArgs / modifiedResult / // sessionSummary) is asserted alongside hook invocation. If a new handler is // added to SessionHooks, add a corresponding test here. @@ -72,6 +73,59 @@ func TestHooksExtendedE2E(t *testing.T) { } }) + t.Run("should invoke userPromptTransformed hook and modify transformed prompt", func(t *testing.T) { + ctx.ConfigureForTest(t) + + var ( + mu sync.Mutex + inputs []copilot.UserPromptTransformedHookInput + ) + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Hooks: &copilot.SessionHooks{ + OnUserPromptTransformed: func(input copilot.UserPromptTransformedHookInput, invocation copilot.HookInvocation) (*copilot.UserPromptTransformedHookOutput, error) { + mu.Lock() + inputs = append(inputs, input) + mu.Unlock() + if invocation.SessionID == "" { + t.Error("Expected non-empty session ID in invocation") + } + return &copilot.UserPromptTransformedHookOutput{ + ModifiedTransformedPrompt: copilot.String("Reply with exactly: HOOKED_TRANSFORMED_PROMPT"), + }, nil + }, + }, + }) + if err != nil { + t.Fatalf("Failed to create session: %v", err) + } + + response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: "Answer the request above."}) + if err != nil { + t.Fatalf("Failed to send message: %v", err) + } + + mu.Lock() + defer mu.Unlock() + if len(inputs) == 0 { + t.Fatal("Expected at least one userPromptTransformed hook invocation") + } + if !strings.Contains(inputs[0].Prompt, "Answer the request above.") { + t.Errorf("Expected original prompt in hook input, got %q", inputs[0].Prompt) + } + if !strings.Contains(inputs[0].TransformedPrompt, "Answer the request above.") || + !strings.Contains(inputs[0].TransformedPrompt, "") { + t.Errorf("Expected runtime-transformed prompt in hook input, got %q", inputs[0].TransformedPrompt) + } + if !inputs[0].Timestamp.After(time.UnixMilli(0)) || inputs[0].WorkingDirectory == "" { + t.Error("Expected timestamp and working directory in hook input") + } + assistantMessage, ok := response.Data.(*copilot.AssistantMessageData) + if !ok || !strings.Contains(assistantMessage.Content, "HOOKED_TRANSFORMED_PROMPT") { + t.Errorf("Expected transformed prompt response, got %v", response.Data) + } + }) + t.Run("should invoke sessionStart hook", func(t *testing.T) { ctx.ConfigureForTest(t) diff --git a/go/session.go b/go/session.go index a30700ce1..99939de4a 100644 --- a/go/session.go +++ b/go/session.go @@ -786,6 +786,16 @@ func (s *Session) handleHooksInvoke(hookType string, rawInput json.RawMessage) ( } return hooks.OnUserPromptSubmitted(input, invocation) + case "userPromptTransformed": + if hooks.OnUserPromptTransformed == nil { + return nil, nil + } + var input UserPromptTransformedHookInput + if err := json.Unmarshal(rawInput, &input); err != nil { + return nil, fmt.Errorf("invalid hook input: %w", err) + } + return hooks.OnUserPromptTransformed(input, invocation) + case "sessionStart": if hooks.OnSessionStart == nil { return nil, nil diff --git a/go/types.go b/go/types.go index d9e92ef50..19689c707 100644 --- a/go/types.go +++ b/go/types.go @@ -679,6 +679,46 @@ type UserPromptSubmittedHookOutput struct { // UserPromptSubmittedHandler handles user-prompt-submitted hook invocations type UserPromptSubmittedHandler func(input UserPromptSubmittedHookInput, invocation HookInvocation) (*UserPromptSubmittedHookOutput, error) +// UserPromptTransformedHookInput is the input for a user-prompt-transformed hook. +type UserPromptTransformedHookInput struct { + SessionID string `json:"sessionId"` + Timestamp time.Time `json:"-"` + WorkingDirectory string `json:"cwd"` + Prompt string `json:"prompt"` + TransformedPrompt string `json:"transformedPrompt"` +} + +// MarshalJSON implements json.Marshaler, emitting Timestamp as Unix milliseconds. +func (h UserPromptTransformedHookInput) MarshalJSON() ([]byte, error) { + type alias UserPromptTransformedHookInput + return json.Marshal(&struct { + Timestamp int64 `json:"timestamp"` + alias + }{Timestamp: h.Timestamp.UnixMilli(), alias: alias(h)}) +} + +// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds. +func (h *UserPromptTransformedHookInput) UnmarshalJSON(data []byte) error { + type alias UserPromptTransformedHookInput + aux := &struct { + Timestamp int64 `json:"timestamp"` + *alias + }{alias: (*alias)(h)} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + h.Timestamp = time.UnixMilli(aux.Timestamp) + return nil +} + +// UserPromptTransformedHookOutput is the output for a user-prompt-transformed hook. +type UserPromptTransformedHookOutput struct { + ModifiedTransformedPrompt *string `json:"modifiedTransformedPrompt,omitempty"` +} + +// UserPromptTransformedHandler handles user-prompt-transformed hook invocations. +type UserPromptTransformedHandler func(input UserPromptTransformedHookInput, invocation HookInvocation) (*UserPromptTransformedHookOutput, error) + // SessionStartHookInput is the input for a session-start hook type SessionStartHookInput struct { SessionID string `json:"sessionId"` @@ -899,15 +939,16 @@ type HookInvocation struct { // SessionHooks configures hook handlers for a session type SessionHooks struct { - OnPreToolUse PreToolUseHandler - OnPostToolUse PostToolUseHandler - OnPostToolUseFailure PostToolUseFailureHandler - OnUserPromptSubmitted UserPromptSubmittedHandler - OnSessionStart SessionStartHandler - OnSessionEnd SessionEndHandler - OnErrorOccurred ErrorOccurredHandler - OnAgentStop AgentStopHandler - OnPreMCPToolCall PreMCPToolCallHandler + OnPreToolUse PreToolUseHandler + OnPostToolUse PostToolUseHandler + OnPostToolUseFailure PostToolUseFailureHandler + OnUserPromptSubmitted UserPromptSubmittedHandler + OnUserPromptTransformed UserPromptTransformedHandler + OnSessionStart SessionStartHandler + OnSessionEnd SessionEndHandler + OnErrorOccurred ErrorOccurredHandler + OnAgentStop AgentStopHandler + OnPreMCPToolCall PreMCPToolCallHandler } // MCPServerConfig is implemented by MCP server configuration types. diff --git a/go/types_test.go b/go/types_test.go index a76ebaad4..4195464b3 100644 --- a/go/types_test.go +++ b/go/types_test.go @@ -5,6 +5,18 @@ import ( "testing" ) +func TestUserPromptTransformedHookOutput_PreservesEmptyReplacement(t *testing.T) { + data, err := json.Marshal(UserPromptTransformedHookOutput{ + ModifiedTransformedPrompt: String(""), + }) + if err != nil { + t.Fatalf("failed to marshal hook output: %v", err) + } + if string(data) != `{"modifiedTransformedPrompt":""}` { + t.Fatalf("expected empty replacement to be preserved, got %s", data) + } +} + func TestProviderConfig_JSONIncludesHeaders(t *testing.T) { config := ProviderConfig{ BaseURL: "https://example.com/provider", diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/src/main/java/com/github/copilot/CopilotSession.java index cb16f55fc..b977b69a1 100644 --- a/java/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/src/main/java/com/github/copilot/CopilotSession.java @@ -110,6 +110,7 @@ import com.github.copilot.rpc.UserInputRequest; import com.github.copilot.rpc.UserInputResponse; import com.github.copilot.rpc.UserPromptSubmittedHookInput; +import com.github.copilot.rpc.UserPromptTransformedHookInput; /** * Represents a single conversation session with the Copilot CLI. @@ -1869,6 +1870,17 @@ CompletableFuture handleHooksInvoke(String hookType, JsonNode input) { return promptResult.thenApply(output -> (Object) output); } break; + case "userPromptTransformed" : + if (hooks.getOnUserPromptTransformed() != null) { + UserPromptTransformedHookInput transformedInput = MAPPER.treeToValue(input, + UserPromptTransformedHookInput.class); + var transformedResult = hooks.getOnUserPromptTransformed().handle(transformedInput, invocation); + if (transformedResult == null) { + return CompletableFuture.completedFuture(null); + } + return transformedResult.thenApply(output -> (Object) output); + } + break; case "sessionStart" : if (hooks.getOnSessionStart() != null) { SessionStartHookInput startInput = MAPPER.treeToValue(input, SessionStartHookInput.class); diff --git a/java/src/main/java/com/github/copilot/rpc/SessionHooks.java b/java/src/main/java/com/github/copilot/rpc/SessionHooks.java index 9cf68684d..e476f888e 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionHooks.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionHooks.java @@ -42,6 +42,7 @@ public class SessionHooks { private PostToolUseHandler onPostToolUse; private PostToolUseFailureHandler onPostToolUseFailure; private UserPromptSubmittedHandler onUserPromptSubmitted; + private UserPromptTransformedHandler onUserPromptTransformed; private SessionStartHandler onSessionStart; private SessionEndHandler onSessionEnd; private AgentStopHandler onAgentStop; @@ -161,6 +162,29 @@ public SessionHooks setOnUserPromptSubmitted(UserPromptSubmittedHandler onUserPr return this; } + /** + * Gets the user-prompt-transformed handler. + * + * @return the handler, or {@code null} if not set + * @since 1.0.11 + */ + public UserPromptTransformedHandler getOnUserPromptTransformed() { + return onUserPromptTransformed; + } + + /** + * Sets the handler called after the runtime transforms a submitted prompt. + * + * @param onUserPromptTransformed + * the handler + * @return this instance for method chaining + * @since 1.0.11 + */ + public SessionHooks setOnUserPromptTransformed(UserPromptTransformedHandler onUserPromptTransformed) { + this.onUserPromptTransformed = onUserPromptTransformed; + return this; + } + /** * Gets the session-start handler. * @@ -237,7 +261,7 @@ public SessionHooks setOnAgentStop(AgentStopHandler onAgentStop) { */ public boolean hasHooks() { return onPreToolUse != null || onPreMcpToolCall != null || onPostToolUse != null || onPostToolUseFailure != null - || onUserPromptSubmitted != null || onSessionStart != null || onSessionEnd != null - || onAgentStop != null; + || onUserPromptSubmitted != null || onUserPromptTransformed != null || onSessionStart != null + || onSessionEnd != null || onAgentStop != null; } } diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java new file mode 100644 index 000000000..ac8496078 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.concurrent.CompletableFuture; + +/** + * Handler for user-prompt-transformed hooks. + * + * @since 1.0.11 + */ +@FunctionalInterface +public interface UserPromptTransformedHandler { + + /** + * Handles a transformed user prompt before it is stored or sent to the model. + * + * @param input + * the hook input + * @param invocation + * metadata about the hook invocation + * @return a future resolving to the hook output, or {@code null} + */ + CompletableFuture handle(UserPromptTransformedHookInput input, + HookInvocation invocation); +} diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java new file mode 100644 index 000000000..ea1759658 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Input for user-prompt-transformed hooks. + * + * @param sessionId + * the runtime session ID + * @param timestamp + * Unix timestamp in milliseconds + * @param cwd + * the current working directory + * @param prompt + * the prompt after user-prompt-submitted hooks + * @param transformedPrompt + * the model-facing prompt after runtime transformations + * @since 1.0.11 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record UserPromptTransformedHookInput(@JsonProperty("sessionId") String sessionId, + @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, + @JsonProperty("prompt") String prompt, @JsonProperty("transformedPrompt") String transformedPrompt) { +} diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java new file mode 100644 index 000000000..615f4ea7b --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Output for user-prompt-transformed hooks. + * + * @param modifiedTransformedPrompt + * replacement model-facing prompt to persist and send to the model + * @since 1.0.11 + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public record UserPromptTransformedHookOutput( + @JsonProperty("modifiedTransformedPrompt") String modifiedTransformedPrompt) { +} diff --git a/java/src/test/java/com/github/copilot/HooksTest.java b/java/src/test/java/com/github/copilot/HooksTest.java index 98cb962fc..c3833891c 100644 --- a/java/src/test/java/com/github/copilot/HooksTest.java +++ b/java/src/test/java/com/github/copilot/HooksTest.java @@ -27,6 +27,8 @@ import com.github.copilot.rpc.PreToolUseHookOutput; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionHooks; +import com.github.copilot.rpc.UserPromptTransformedHookInput; +import com.github.copilot.rpc.UserPromptTransformedHookOutput; /** * Tests for hooks functionality (pre-tool-use and post-tool-use hooks). @@ -267,4 +269,34 @@ void testInvokeAgentStopHookAndApplyBlockResponse() throws Exception { assertTrue(response.getData().content().contains("AGENT_STOP_CONTINUED")); } } + + @Test + void testInvokeUserPromptTransformedHookAndModifyTransformedPrompt() throws Exception { + ctx.configureForTest("hooks_extended", + "should_invoke_userprompttransformed_hook_and_modify_transformed_prompt"); + + var inputs = new ArrayList(); + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnUserPromptTransformed((input, invocation) -> { + assertFalse(invocation.getSessionId().isBlank()); + inputs.add(input); + return CompletableFuture.completedFuture( + new UserPromptTransformedHookOutput("Reply with exactly: HOOKED_TRANSFORMED_PROMPT")); + })); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession(config).get(); + var response = session.sendAndWait(new MessageOptions().setPrompt("Answer the request above.")).get(60, + TimeUnit.SECONDS); + + assertFalse(inputs.isEmpty()); + assertTrue(inputs.get(0).prompt().contains("Answer the request above.")); + assertTrue(inputs.get(0).transformedPrompt().contains("Answer the request above.")); + assertTrue(inputs.get(0).transformedPrompt().contains("")); + assertTrue(inputs.get(0).timestamp() > 0); + assertFalse(inputs.get(0).cwd().isBlank()); + assertNotNull(response); + assertTrue(response.getData().content().contains("HOOKED_TRANSFORMED_PROMPT")); + } + } } diff --git a/java/src/test/java/com/github/copilot/SessionHandlerTest.java b/java/src/test/java/com/github/copilot/SessionHandlerTest.java index 05994df8d..345fdccff 100644 --- a/java/src/test/java/com/github/copilot/SessionHandlerTest.java +++ b/java/src/test/java/com/github/copilot/SessionHandlerTest.java @@ -26,6 +26,7 @@ import com.github.copilot.rpc.UserInputRequest; import com.github.copilot.rpc.UserInputResponse; import com.github.copilot.rpc.UserPromptSubmittedHookOutput; +import com.github.copilot.rpc.UserPromptTransformedHookOutput; /** * Unit tests for CopilotSession internal handler methods. @@ -225,6 +226,26 @@ void testHandleHooksInvokeUserPromptSubmitted() throws Exception { assertEquals("modified prompt", output.modifiedPrompt()); } + @Test + void testHandleHooksInvokeUserPromptTransformed() throws Exception { + var hooks = new SessionHooks().setOnUserPromptTransformed((hookInput, invocation) -> { + assertEquals("handler-test-session", invocation.getSessionId()); + assertEquals("original prompt", hookInput.prompt()); + assertEquals("transformed prompt", hookInput.transformedPrompt()); + return CompletableFuture.completedFuture(new UserPromptTransformedHookOutput("replacement prompt")); + }); + session.registerHooks(hooks); + + JsonNode input = MAPPER.valueToTree(Map.of("sessionId", "runtime-session", "timestamp", 1735689600L, "cwd", + "/tmp", "prompt", "original prompt", "transformedPrompt", "transformed prompt")); + + Object result = session.handleHooksInvoke("userPromptTransformed", input).get(); + + assertInstanceOf(UserPromptTransformedHookOutput.class, result); + var output = (UserPromptTransformedHookOutput) result; + assertEquals("replacement prompt", output.modifiedTransformedPrompt()); + } + // ===== handleHooksInvoke: sessionStart ===== @Test diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 8d79a71a1..f915a8707 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -63,6 +63,9 @@ export type { AgentStopHandler, AgentStopHookInput, AgentStopHookOutput, + UserPromptTransformedHandler, + UserPromptTransformedHookInput, + UserPromptTransformedHookOutput, CopilotClientMode, CopilotClientOptions, CopilotExpAssignmentResponse, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 5985d1763..ed575a515 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -1881,6 +1881,7 @@ export class CopilotSession { postToolUse: this.hooks.onPostToolUse as GenericHandler | undefined, postToolUseFailure: this.hooks.onPostToolUseFailure as GenericHandler | undefined, userPromptSubmitted: this.hooks.onUserPromptSubmitted as GenericHandler | undefined, + userPromptTransformed: this.hooks.onUserPromptTransformed as GenericHandler | undefined, sessionStart: this.hooks.onSessionStart as GenericHandler | undefined, sessionEnd: this.hooks.onSessionEnd as GenericHandler | undefined, errorOccurred: this.hooks.onErrorOccurred as GenericHandler | undefined, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 0c15d2df4..b3d572496 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -1437,6 +1437,33 @@ export type UserPromptSubmittedHandler = ( invocation: { sessionId: string } ) => Promise | UserPromptSubmittedHookOutput | void; +/** + * Input for the user-prompt-transformed hook. + * + * This hook runs after the runtime has transformed the submitted prompt with + * generated context, but before it is persisted to session history or sent to + * the model. + */ +export interface UserPromptTransformedHookInput extends BaseHookInput { + prompt: string; + transformedPrompt: string; +} + +/** + * Output for the user-prompt-transformed hook. + */ +export interface UserPromptTransformedHookOutput { + modifiedTransformedPrompt?: string; +} + +/** + * Handler for the user-prompt-transformed hook. + */ +export type UserPromptTransformedHandler = ( + input: UserPromptTransformedHookInput, + invocation: { sessionId: string } +) => Promise | UserPromptTransformedHookOutput | void; + /** * Input for session-start hook */ @@ -1593,6 +1620,11 @@ export interface SessionHooks { */ onUserPromptSubmitted?: UserPromptSubmittedHandler; + /** + * Called after the runtime transforms a submitted prompt and before it is stored. + */ + onUserPromptTransformed?: UserPromptTransformedHandler; + /** * Called when a session starts */ diff --git a/nodejs/test/e2e/hooks_extended.e2e.test.ts b/nodejs/test/e2e/hooks_extended.e2e.test.ts index 5b997adb2..3ac858650 100644 --- a/nodejs/test/e2e/hooks_extended.e2e.test.ts +++ b/nodejs/test/e2e/hooks_extended.e2e.test.ts @@ -14,6 +14,7 @@ import type { SessionEndHookInput, SessionStartHookInput, UserPromptSubmittedHookInput, + UserPromptTransformedHookInput, } from "../../src/types.js"; import { createSdkTestContext } from "./harness/sdkTestContext.js"; @@ -169,6 +170,36 @@ describe("Extended session hooks", async () => { await session.disconnect(); }); + it("should invoke userPromptTransformed hook and modify transformed prompt", async () => { + const inputs: UserPromptTransformedHookInput[] = []; + const session = await client.createSession({ + onPermissionRequest: approveAll, + hooks: { + onUserPromptTransformed: async (input, invocation) => { + inputs.push(input); + expect(invocation.sessionId).toBeTruthy(); + return { + modifiedTransformedPrompt: "Reply with exactly: HOOKED_TRANSFORMED_PROMPT", + }; + }, + }, + }); + + const response = await session.sendAndWait({ + prompt: "Answer the request above.", + }); + + expect(inputs.length).toBeGreaterThan(0); + expect(inputs[0].prompt).toContain("Answer the request above."); + expect(inputs[0].transformedPrompt).toContain("Answer the request above."); + expect(inputs[0].transformedPrompt).toContain(""); + expect(inputs[0].timestamp).toBeInstanceOf(Date); + expect(inputs[0].workingDirectory).toBeDefined(); + expect(response?.data.content ?? "").toContain("HOOKED_TRANSFORMED_PROMPT"); + + await session.disconnect(); + }); + it("should invoke sessionStart hook", async () => { const inputs: SessionStartHookInput[] = []; const invocationSessionIds: string[] = []; diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 8b0100df2..678fffbf1 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -173,6 +173,9 @@ UserPromptSubmittedHandler, UserPromptSubmittedHookInput, UserPromptSubmittedHookOutput, + UserPromptTransformedHandler, + UserPromptTransformedHookInput, + UserPromptTransformedHookOutput, ) from .session_fs_provider import ( SessionFsFileInfo, @@ -362,6 +365,9 @@ "UserPromptSubmittedHandler", "UserPromptSubmittedHookInput", "UserPromptSubmittedHookOutput", + "UserPromptTransformedHandler", + "UserPromptTransformedHookInput", + "UserPromptTransformedHookOutput", "convert_mcp_call_tool_result", "create_session_fs_adapter", "define_tool", diff --git a/python/copilot/session.py b/python/copilot/session.py index 193c571b3..92c24bdd8 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -957,6 +957,28 @@ class UserPromptSubmittedHookOutput(TypedDict, total=False): ] +class UserPromptTransformedHookInput(TypedDict): + """Input for the user-prompt-transformed hook.""" + + sessionId: str + timestamp: datetime + workingDirectory: str + prompt: str + transformedPrompt: str + + +class UserPromptTransformedHookOutput(TypedDict, total=False): + """Output for the user-prompt-transformed hook.""" + + modifiedTransformedPrompt: str + + +UserPromptTransformedHandler = Callable[ + [UserPromptTransformedHookInput, dict[str, str]], + UserPromptTransformedHookOutput | None | Awaitable[UserPromptTransformedHookOutput | None], +] + + class SessionStartHookInput(TypedDict): """Input for session-start hook""" @@ -1063,6 +1085,7 @@ class SessionHooks(TypedDict, total=False): on_post_tool_use: PostToolUseHandler on_post_tool_use_failure: PostToolUseFailureHandler on_user_prompt_submitted: UserPromptSubmittedHandler + on_user_prompt_transformed: UserPromptTransformedHandler on_session_start: SessionStartHandler on_session_end: SessionEndHandler on_error_occurred: ErrorOccurredHandler @@ -2794,6 +2817,7 @@ async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: "postToolUse": hooks.get("on_post_tool_use"), "postToolUseFailure": hooks.get("on_post_tool_use_failure"), "userPromptSubmitted": hooks.get("on_user_prompt_submitted"), + "userPromptTransformed": hooks.get("on_user_prompt_transformed"), "sessionStart": hooks.get("on_session_start"), "sessionEnd": hooks.get("on_session_end"), "errorOccurred": hooks.get("on_error_occurred"), diff --git a/python/e2e/test_hooks_extended_e2e.py b/python/e2e/test_hooks_extended_e2e.py index b38534ea2..7af20f32b 100644 --- a/python/e2e/test_hooks_extended_e2e.py +++ b/python/e2e/test_hooks_extended_e2e.py @@ -3,7 +3,8 @@ E2E coverage for every handler exposed on ``SessionHooks``: ``on_pre_tool_use``, ``on_post_tool_use``, ``on_post_tool_use_failure``, -``on_user_prompt_submitted``, ``on_session_start``, ``on_session_end``, +``on_user_prompt_submitted``, ``on_user_prompt_transformed``, ``on_session_start``, +``on_session_end``, ``on_error_occurred``, ``on_agent_stop``. Output-shape behavior (modifiedPrompt / additionalContext / errorHandling / modifiedArgs / modifiedResult / sessionSummary) is asserted alongside hook invocation. @@ -48,6 +49,32 @@ async def on_user_prompt_submitted(input_data, invocation): finally: await session.disconnect() + async def test_should_invoke_userprompttransformed_hook_and_modify_transformed_prompt( + self, ctx: E2ETestContext + ): + inputs: list[dict] = [] + + async def on_user_prompt_transformed(input_data, invocation): + assert invocation["session_id"] + inputs.append(input_data) + return {"modifiedTransformedPrompt": "Reply with exactly: HOOKED_TRANSFORMED_PROMPT"} + + session = await ctx.client.create_session( + on_permission_request=PermissionHandler.approve_all, + hooks={"on_user_prompt_transformed": on_user_prompt_transformed}, + ) + try: + response = await session.send_and_wait("Answer the request above.") + assert inputs + assert "Answer the request above." in inputs[0]["prompt"] + assert "Answer the request above." in inputs[0]["transformedPrompt"] + assert "" in inputs[0]["transformedPrompt"] + assert inputs[0]["timestamp"].timestamp() > 0 + assert inputs[0]["workingDirectory"] + assert "HOOKED_TRANSFORMED_PROMPT" in (response.data.content or "") + finally: + await session.disconnect() + async def test_should_invoke_sessionstart_hook(self, ctx: E2ETestContext): inputs: list[dict] = [] invocation_session_ids: list[str] = [] diff --git a/rust/README.md b/rust/README.md index 0bdaa400c..314090044 100644 --- a/rust/README.md +++ b/rust/README.md @@ -320,7 +320,7 @@ let session = client .await?; ``` -**Hook events:** `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmitted`, `SessionStart`, `SessionEnd`, `ErrorOccurred`. Each carries typed input/output structs. `PostToolUse` only fires on success; override `on_post_tool_use_failure` to observe failed tool calls. Return `HookOutput::None` for events you don't handle. +**Hook events:** `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmitted`, `UserPromptTransformed`, `SessionStart`, `SessionEnd`, `ErrorOccurred`. Each carries typed input/output structs. `PostToolUse` only fires on success; override `on_post_tool_use_failure` to observe failed tool calls. Return `HookOutput::None` for events you don't handle. ### System Message Transforms diff --git a/rust/src/hooks.rs b/rust/src/hooks.rs index a2b61ed8b..4986d6cb1 100644 --- a/rust/src/hooks.rs +++ b/rust/src/hooks.rs @@ -199,6 +199,32 @@ pub struct UserPromptSubmittedOutput { pub suppress_output: Option, } +/// Input for the `userPromptTransformed` hook. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserPromptTransformedInput { + /// The runtime session ID of the session that triggered the hook. + pub session_id: String, + /// Unix timestamp in ms. + pub timestamp: f64, + /// Working directory. + #[serde(rename = "cwd")] + pub working_directory: PathBuf, + /// The prompt after any `userPromptSubmitted` hooks have run. + pub prompt: String, + /// The model-facing prompt after runtime transformations. + pub transformed_prompt: String, +} + +/// Output for the `userPromptTransformed` hook. +#[derive(Debug, Clone, Default, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UserPromptTransformedOutput { + /// Replacement model-facing prompt to persist and send to the model. + #[serde(skip_serializing_if = "Option::is_none")] + pub modified_transformed_prompt: Option, +} + /// Input for the `sessionStart` hook. #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -381,6 +407,13 @@ pub enum HookEvent { /// Session context. ctx: HookContext, }, + /// Fired after the runtime transforms a submitted prompt. + UserPromptTransformed { + /// Typed input data. + input: UserPromptTransformedInput, + /// Session context. + ctx: HookContext, + }, /// Fired at session creation or resume. SessionStart { /// Typed input data. @@ -430,6 +463,8 @@ pub enum HookOutput { PostToolUseFailure(PostToolUseFailureOutput), /// Response for a user-prompt-submitted hook. UserPromptSubmitted(UserPromptSubmittedOutput), + /// Response for a user-prompt-transformed hook. + UserPromptTransformed(UserPromptTransformedOutput), /// Response for a session-start hook. SessionStart(SessionStartOutput), /// Response for a session-end hook. @@ -449,6 +484,7 @@ impl HookOutput { Self::PostToolUse(_) => "PostToolUse", Self::PostToolUseFailure(_) => "PostToolUseFailure", Self::UserPromptSubmitted(_) => "UserPromptSubmitted", + Self::UserPromptTransformed(_) => "UserPromptTransformed", Self::SessionStart(_) => "SessionStart", Self::SessionEnd(_) => "SessionEnd", Self::ErrorOccurred(_) => "ErrorOccurred", @@ -506,6 +542,11 @@ pub trait SessionHooks: Send + Sync + 'static { .await .map(HookOutput::UserPromptSubmitted) .unwrap_or(HookOutput::None), + HookEvent::UserPromptTransformed { input, ctx } => self + .on_user_prompt_transformed(input, ctx) + .await + .map(HookOutput::UserPromptTransformed) + .unwrap_or(HookOutput::None), HookEvent::SessionStart { input, ctx } => self .on_session_start(input, ctx) .await @@ -583,6 +624,16 @@ pub trait SessionHooks: Send + Sync + 'static { None } + /// Called after the runtime transforms a submitted prompt. Return + /// `Some(output)` to replace the model-facing content before it is stored. + async fn on_user_prompt_transformed( + &self, + _input: UserPromptTransformedInput, + _ctx: HookContext, + ) -> Option { + None + } + /// Called at session creation or resume. Return `Some(output)` to /// inject startup context. async fn on_session_start( @@ -660,6 +711,10 @@ pub(crate) async fn dispatch_hook( let input: UserPromptSubmittedInput = serde_json::from_value(raw_input)?; HookEvent::UserPromptSubmitted { input, ctx } } + "userPromptTransformed" => { + let input: UserPromptTransformedInput = serde_json::from_value(raw_input)?; + HookEvent::UserPromptTransformed { input, ctx } + } "sessionStart" => { let input: SessionStartInput = serde_json::from_value(raw_input)?; HookEvent::SessionStart { input, ctx } @@ -708,6 +763,9 @@ pub(crate) async fn dispatch_hook( ("userPromptSubmitted", HookOutput::UserPromptSubmitted(o)) => { Some(serde_json::to_value(o)?) } + ("userPromptTransformed", HookOutput::UserPromptTransformed(o)) => { + Some(serde_json::to_value(o)?) + } ("sessionStart", HookOutput::SessionStart(o)) => Some(serde_json::to_value(o)?), ("sessionEnd", HookOutput::SessionEnd(o)) => Some(serde_json::to_value(o)?), ("errorOccurred", HookOutput::ErrorOccurred(o)) => Some(serde_json::to_value(o)?), @@ -753,6 +811,14 @@ mod tests { ..Default::default() }) } + HookEvent::UserPromptTransformed { input, .. } => { + HookOutput::UserPromptTransformed(UserPromptTransformedOutput { + modified_transformed_prompt: Some(format!( + "[transformed] {}", + input.transformed_prompt + )), + }) + } _ => HookOutput::None, } } @@ -813,6 +879,30 @@ mod tests { assert_eq!(result["output"]["modifiedPrompt"], "[prefixed] hello world"); } + #[tokio::test] + async fn dispatch_user_prompt_transformed() { + let hooks = TestHooks; + let input = serde_json::json!({ + "sessionId": "sess-1", + "timestamp": 1234567890, + "cwd": "/tmp", + "prompt": "hello world", + "transformedPrompt": "now\nhello world" + }); + let result = dispatch_hook( + &hooks, + &SessionId::new("sess-1"), + "userPromptTransformed", + input, + ) + .await + .unwrap(); + assert_eq!( + result["output"]["modifiedTransformedPrompt"], + "[transformed] now\nhello world" + ); + } + #[tokio::test] async fn dispatch_unregistered_hook_returns_empty() { let hooks = TestHooks; diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs index b18460642..dfd77ed7c 100644 --- a/rust/tests/e2e/hooks_extended.rs +++ b/rust/tests/e2e/hooks_extended.rs @@ -8,6 +8,7 @@ use github_copilot_sdk::hooks::{ PostToolUseFailureInput, PostToolUseFailureOutput, PostToolUseInput, PostToolUseOutput, PreToolUseInput, PreToolUseOutput, SessionEndInput, SessionEndOutput, SessionHooks, SessionStartInput, SessionStartOutput, UserPromptSubmittedInput, UserPromptSubmittedOutput, + UserPromptTransformedInput, UserPromptTransformedOutput, }; use github_copilot_sdk::tool::ToolHandler; use github_copilot_sdk::{Error, SessionConfig, Tool, ToolInvocation, ToolResult}; @@ -189,6 +190,50 @@ async fn should_invoke_userpromptsubmitted_hook_and_modify_prompt() { .await; } +#[tokio::test] +async fn should_invoke_userprompttransformed_hook_and_modify_transformed_prompt() { + super::support::with_shared_e2e_context( + &E2E, + "hooks_extended", + "should_invoke_userprompttransformed_hook_and_modify_transformed_prompt", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let (tx, mut rx) = mpsc::unbounded_channel(); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_hooks(Arc::new(UserPromptTransformedHooks { tx })), + ) + .await + .expect("create session"); + + let answer = session + .send_and_wait("Answer the request above.") + .await + .expect("send") + .expect("assistant message"); + let input = recv_with_timeout(&mut rx, "userPromptTransformed hook").await; + assert!(input.prompt.contains("Answer the request above.")); + assert!( + input + .transformed_prompt + .contains("Answer the request above.") + ); + assert!(input.transformed_prompt.contains("")); + assert!(input.timestamp > 0.0); + assert!(!input.working_directory.as_os_str().is_empty()); + assert!(assistant_message_content(&answer).contains("HOOKED_TRANSFORMED_PROMPT")); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) + .await; +} + #[tokio::test] async fn should_invoke_sessionstart_hook() { super::support::with_shared_e2e_context( @@ -508,6 +553,27 @@ struct AgentStopHooks { call_count: AtomicUsize, } +struct UserPromptTransformedHooks { + tx: mpsc::UnboundedSender, +} + +#[async_trait] +impl SessionHooks for UserPromptTransformedHooks { + async fn on_user_prompt_transformed( + &self, + input: UserPromptTransformedInput, + ctx: HookContext, + ) -> Option { + assert!(!ctx.session_id.as_str().is_empty()); + let _ = self.tx.send(input); + Some(UserPromptTransformedOutput { + modified_transformed_prompt: Some( + "Reply with exactly: HOOKED_TRANSFORMED_PROMPT".to_string(), + ), + }) + } +} + #[async_trait] impl SessionHooks for AgentStopHooks { async fn on_agent_stop( @@ -739,4 +805,4 @@ impl ToolHandler for EchoValueTool { } } static E2E: super::support::SharedE2eGroup = - super::support::SharedE2eGroup::standard("hooks_extended", 11); + super::support::SharedE2eGroup::standard("hooks_extended", 12); diff --git a/test/snapshots/hooks_extended/should_invoke_userprompttransformed_hook_and_modify_transformed_prompt.yaml b/test/snapshots/hooks_extended/should_invoke_userprompttransformed_hook_and_modify_transformed_prompt.yaml new file mode 100644 index 000000000..db2b02968 --- /dev/null +++ b/test/snapshots/hooks_extended/should_invoke_userprompttransformed_hook_and_modify_transformed_prompt.yaml @@ -0,0 +1,10 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: "Reply with exactly: HOOKED_TRANSFORMED_PROMPT" + - role: assistant + content: HOOKED_TRANSFORMED_PROMPT From e7876395f12a6dcc375a84b11ac73ea86667e3ec Mon Sep 17 00:00:00 2001 From: Rince Yuan Date: Wed, 5 Aug 2026 08:41:35 +0800 Subject: [PATCH 32/72] fix: Java README version stuck at 1.0.5-01; release sed regex can't match numeric qualifiers (#2226) * fix: Java README version stuck at 1.0.5-01; release sed regex can't match numeric qualifiers The qualifier group \(-[a-z]...)\ requires qualifiers to start with a letter. Numeric-only qualifiers like \-01\ don't match, so the Maven snippet was never updated past 1.0.5-01. Fix the regex to accept any alphanumeric qualifier start character: \(-[a-zA-Z0-9][a-zA-Z0-9.-]*)\ Also update the stale README versions to 1.0.6 (latest release). Closes #2209 * test(java): validate documentation version updates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a90becbd-1667-4e13-8047-b5715ad21e9f --------- Co-authored-by: j-zhangyiyuan Co-authored-by: Ed Burns Copilot-Session: a90becbd-1667-4e13-8047-b5715ad21e9f --- .github/workflows/java-publish-maven.yml | 16 +--- .github/workflows/java-sdk-tests.yml | 4 + java/README.md | 4 +- java/jbang-example.java | 2 +- .../test-update-documentation-versions.sh | 53 ++++++++++++ java/scripts/update-documentation-versions.sh | 80 +++++++++++++++++++ 6 files changed, 142 insertions(+), 17 deletions(-) create mode 100755 java/scripts/test-update-documentation-versions.sh create mode 100755 java/scripts/update-documentation-versions.sh diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml index 944a0ee38..e293d9127 100644 --- a/.github/workflows/java-publish-maven.yml +++ b/.github/workflows/java-publish-maven.yml @@ -199,21 +199,9 @@ jobs: working-directory: ./java run: | VERSION="${{ steps.versions.outputs.release_version }}" - - # Update release version in README.md (supports any version qualifier like -java.N, -java-preview.N, -beta-java.N) - sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|${VERSION}|g" README.md - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|copilot-sdk-java:${VERSION}|g" README.md - - # Update snapshot versions in README.md (must run AFTER release version seds - # because the release copilot-sdk-java: pattern partially matches inside snapshot - # strings — the snapshot-specific seds override with the correct DEV_VERSION) DEV_VERSION="${{ steps.versions.outputs.dev_version }}" - sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*-SNAPSHOT|${DEV_VERSION}|g" README.md - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*-SNAPSHOT|copilot-sdk-java:${DEV_VERSION}|g" README.md - - # Update version in jbang-example.java - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|copilot-sdk-java:${VERSION}|g" jbang-example.java - sed -i 's|copilot-sdk-java:${project\.version}|copilot-sdk-java:'"${VERSION}"'|g' jbang-example.java + ./scripts/test-update-documentation-versions.sh + ./scripts/update-documentation-versions.sh "$VERSION" "$DEV_VERSION" README.md jbang-example.java # Commit the documentation changes before release:prepare (requires clean working directory) git add README.md jbang-example.java diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index c92b5291a..20083749e 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -45,6 +45,10 @@ jobs: with: node-version: 22 + - name: Test documentation version updater + if: matrix.test-jdk == '25' + run: ./scripts/test-update-documentation-versions.sh + - name: Build SDK and set up test harness run: mvn test-compile jar:jar diff --git a/java/README.md b/java/README.md index 87a0ef7c5..c3428eee0 100644 --- a/java/README.md +++ b/java/README.md @@ -32,14 +32,14 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. com.github copilot-sdk-java - 1.0.5-01 + 1.0.9-preview.3 ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.9-preview.3-01' +implementation 'com.github:copilot-sdk-java:1.0.9-preview.3' ``` #### Snapshot Builds diff --git a/java/jbang-example.java b/java/jbang-example.java index 59e625724..9abc39edd 100644 --- a/java/jbang-example.java +++ b/java/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.9-preview.3-01 +//DEPS com.github:copilot-sdk-java:1.0.9-preview.3 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; diff --git a/java/scripts/test-update-documentation-versions.sh b/java/scripts/test-update-documentation-versions.sh new file mode 100755 index 000000000..213bda6c4 --- /dev/null +++ b/java/scripts/test-update-documentation-versions.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +UPDATER="${SCRIPT_DIR}/update-documentation-versions.sh" +TEMP_DIR=$(mktemp -d) +trap 'rm -rf "$TEMP_DIR"' EXIT + +run_case() { + local name=$1 + local old_version=$2 + local old_dev_version=$3 + local version=$4 + local dev_version=$5 + local old_jbang_version=$6 + local case_dir="${TEMP_DIR}/${name}" + + mkdir "$case_dir" + printf '%s\n' \ + '' \ + ' copilot-sdk-java' \ + " ${old_version}" \ + '' \ + "implementation 'com.github:copilot-sdk-java:${old_version}'" \ + '' \ + ' copilot-sdk-java' \ + " ${old_dev_version}" \ + '' \ + "implementation 'com.github:copilot-sdk-java:${old_dev_version}'" \ + > "${case_dir}/README.md" + printf '%s\n' \ + "///usr/bin/env jbang \"\$0\" \"\$@\" ; exit \$?" \ + "//DEPS com.github:copilot-sdk-java:${old_jbang_version}" \ + > "${case_dir}/jbang-example.java" + + "$UPDATER" "$version" "$dev_version" "${case_dir}/README.md" "${case_dir}/jbang-example.java" + + grep -Fqx " ${version}" "${case_dir}/README.md" + grep -Fqx "implementation 'com.github:copilot-sdk-java:${version}'" "${case_dir}/README.md" + grep -Fqx " ${dev_version}" "${case_dir}/README.md" + grep -Fqx "implementation 'com.github:copilot-sdk-java:${dev_version}'" "${case_dir}/README.md" + grep -Fqx "//DEPS com.github:copilot-sdk-java:${version}" "${case_dir}/jbang-example.java" + + if grep -Fq "$old_version" "${case_dir}/README.md" "${case_dir}/jbang-example.java" || + grep -Fq "$old_dev_version" "${case_dir}/README.md"; then + echo "Stale version remained in ${name} test output" >&2 + exit 1 + fi +} + +run_case stable 1.0.8 1.0.9-SNAPSHOT 1.0.9 1.0.10-SNAPSHOT "\${project.version}" +run_case preview 1.0.9-preview.2-01 1.0.10-preview.2-SNAPSHOT 1.0.10-preview.2 1.0.11-preview.2-SNAPSHOT 1.0.9-preview.2-01 diff --git a/java/scripts/update-documentation-versions.sh b/java/scripts/update-documentation-versions.sh new file mode 100755 index 000000000..d2d23c4ff --- /dev/null +++ b/java/scripts/update-documentation-versions.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [[ $# -ne 4 ]]; then + echo "Usage: $0 " >&2 + exit 2 +fi + +VERSION=$1 +DEV_VERSION=$2 +README=$3 +JBANG_EXAMPLE=$4 +VERSION_FORMAT='[0-9]+\.[0-9]+\.[0-9]+(-(preview|(beta-)?java(-preview)?)\.[0-9]+)?' + +if [[ ! "$VERSION" =~ ^${VERSION_FORMAT}$ ]]; then + echo "Invalid release version: $VERSION" >&2 + exit 2 +fi +if [[ ! "$DEV_VERSION" =~ ^${VERSION_FORMAT}-SNAPSHOT$ ]]; then + echo "Invalid development version: $DEV_VERSION" >&2 + exit 2 +fi +if [[ ! -f "$README" || ! -f "$JBANG_EXAMPLE" ]]; then + echo "README and JBang example files must exist" >&2 + exit 2 +fi + +export VERSION DEV_VERSION + +perl -0 - "$README" <<'PERL' +use strict; +use warnings; + +my ($path) = @ARGV; +open my $input, '<', $path or die "Cannot read $path: $!\n"; +my $content = do { local $/; <$input> }; +close $input or die "Cannot close $path: $!\n"; + +# Match accepted release versions plus numeric suffixes left by the former broken updater. +my $version = qr/[0-9]+\.[0-9]+\.[0-9]+(?:-(?:preview|(?:beta-)?java(?:-preview)?)\.[0-9]+)?(?:-[0-9]+)*/; +my $snapshot_xml = ($content =~ s{$version-SNAPSHOT}{$ENV{DEV_VERSION}}g); +my $snapshot_gradle = ($content =~ s{(copilot-sdk-java:)$version-SNAPSHOT(?![-A-Za-z0-9.])}{$1 . $ENV{DEV_VERSION}}ge); +my $release_xml = ($content =~ s{$version}{$ENV{VERSION}}g); +my $release_gradle = ($content =~ s{(copilot-sdk-java:)$version(?![-A-Za-z0-9.])}{$1 . $ENV{VERSION}}ge); + +die "Expected one release and one snapshot example for both Maven and Gradle in $path\n" + unless $snapshot_xml == 1 && $snapshot_gradle == 1 && $release_xml == 1 && $release_gradle == 1; + +open my $output, '>', $path or die "Cannot write $path: $!\n"; +print {$output} $content; +close $output or die "Cannot close $path: $!\n"; +PERL + +perl -0 - "$JBANG_EXAMPLE" <<'PERL' +use strict; +use warnings; + +my ($path) = @ARGV; +open my $input, '<', $path or die "Cannot read $path: $!\n"; +my $content = do { local $/; <$input> }; +close $input or die "Cannot close $path: $!\n"; + +my $version = qr/[0-9]+\.[0-9]+\.[0-9]+(?:-(?:preview|(?:beta-)?java(?:-preview)?)\.[0-9]+)?(?:-[0-9]+)*/; +my $version_count = ($content =~ s{(copilot-sdk-java:)$version(?![-A-Za-z0-9.])}{$1 . $ENV{VERSION}}ge); +my $placeholder_count = ($content =~ s{copilot-sdk-java:\$\{project\.version\}}{copilot-sdk-java:$ENV{VERSION}}g); + +die "Expected exactly one Copilot SDK dependency in $path\n" + unless $version_count + $placeholder_count == 1; + +open my $output, '>', $path or die "Cannot write $path: $!\n"; +print {$output} $content; +close $output or die "Cannot close $path: $!\n"; +PERL + +grep -Fqx " ${VERSION}" "$README" +grep -Fqx "implementation 'com.github:copilot-sdk-java:${VERSION}'" "$README" +grep -Fqx " ${DEV_VERSION}" "$README" +grep -Fqx "implementation 'com.github:copilot-sdk-java:${DEV_VERSION}'" "$README" +grep -Fqx "//DEPS com.github:copilot-sdk-java:${VERSION}" "$JBANG_EXAMPLE" From 737551be6b7392529f1e25ef8c1bb3a913c0d114 Mon Sep 17 00:00:00 2001 From: Scott Addie <10702007+scottaddie@users.noreply.github.com> Date: Wed, 5 Aug 2026 10:53:50 -0500 Subject: [PATCH 33/72] docs: update Go and Rust API links (#2266) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 32ce3c59-1e52-4cbd-bdfd-35f638bfd81e --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ad2b7127..b2ef69d05 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ The GitHub Copilot SDK exposes the same engine behind Copilot CLI: a production- | ------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | | **Node.js / TypeScript** | [`nodejs/`](./nodejs/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/nodejs/README.md) | `npm install @github/copilot-sdk` | | | **Python** | [`python/`](./python/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/python/README.md) | `pip install github-copilot-sdk` | | -| **Go** | [`go/`](./go/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/go/README.md) | `go get github.com/github/copilot-sdk/go` | [API docs](https://pkg.go.dev/github.com/github/copilot-sdk/go) | +| **Go** | [`go/`](./go/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/go/README.md) | `go get github.com/github/copilot-sdk/go` | [API docs](https://pkg.go.dev/github.com/github/copilot-sdk/go#readme-api-reference) | | **.NET** | [`dotnet/`](./dotnet/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/dotnet/README.md) | `dotnet add package GitHub.Copilot.SDK` | | -| **Rust** | [`rust/`](./rust/) | — | `cargo add github-copilot-sdk` | [API docs](https://docs.rs/github-copilot-sdk/latest/github_copilot_sdk/) | +| **Rust** | [`rust/`](./rust/) | — | `cargo add github-copilot-sdk` | [API docs](https://docs.rs/github-copilot-sdk/latest/github_copilot_sdk/#api-reference) | | **Java** | [`java/`](./java/) | [Cookbook](https://github.com/github/awesome-copilot/blob/main/cookbook/copilot-sdk/java/README.md) | Maven coordinates
`com.github:copilot-sdk-java`
See instructions for [Maven](./java/README.md#maven) and [Gradle](./java/README.md#gradle) | [API docs](https://javadoc.io/doc/com.github/copilot-sdk-java/latest/) | See the individual SDK READMEs for installation, usage examples, and API reference. From 6ce0afc861247faf8d58b108a8fd7e2978ca87c1 Mon Sep 17 00:00:00 2001 From: Patrick Nikoletich Date: Wed, 5 Aug 2026 09:39:24 -0700 Subject: [PATCH 34/72] docs: add citations guide (#2267) * docs: add citations guide Add docs/features/citations.md covering the experimental citations surface: enabling `enableCitations` on session create/resume in all six SDKs, reading the `citations` payload from `assistant.message` events, the payload/type reference, how to supply citable material via document attachments or tool `citableSources`, and current limitations. Link the new guide from the features index. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0c184f59-0104-4a89-a350-4096597a89b3 * docs: address review feedback on citations guide * Fix UTF-16 span offset handling. Python slices by code point and Go and Rust strings are UTF-8, so all three examples now convert the content to UTF-16 code units before slicing. Corrected the accompanying tip, which wrongly implied only non-BMP characters were affected in Go and Rust. * Replace the Rust example's raw JSON dump with a real span-to-source walk using typed_data::(), matching the other languages. * Fall back through url, path, and id when a CitationSource has no title, in the Java, .NET, Go, Python, and TypeScript examples. * Narrow the image-input.md link descriptions, since that guide covers image attachments rather than document support. * Drop the "(experimental)" suffix from the features index entry; the guide body still carries the experimental warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0c184f59-0104-4a89-a350-4096597a89b3 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0c184f59-0104-4a89-a350-4096597a89b3 --- docs/features/README.md | 1 + docs/features/citations.md | 443 +++++++++++++++++++++++++++++++++++++ 2 files changed, 444 insertions(+) create mode 100644 docs/features/citations.md diff --git a/docs/features/README.md b/docs/features/README.md index ef9ae996d..9f3b937c9 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -16,6 +16,7 @@ These guides cover the capabilities you can add to your Copilot SDK application. | [Skills](./skills.md) | Load reusable prompt modules from directories | | [Plugin Directories](./plugin-directories.md) | Bundle skills, hooks, MCP servers, and agents as a single loadable plugin | | [Session limits](./session-limits.md) | Set an AI Credits budget for a session and observe budget events | +| [Citations](./citations.md) | Link assistant responses back to their supporting sources | | [Image Input](./image-input.md) | Send images to sessions as attachments | | [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) | | [Usage and Billing](./usage-and-billing.md) | Read token counts, context-window utilization, AI credit cost, and account quota | diff --git a/docs/features/citations.md b/docs/features/citations.md new file mode 100644 index 000000000..b68ae292c --- /dev/null +++ b/docs/features/citations.md @@ -0,0 +1,443 @@ +# Citations + +Citations link spans of an assistant response back to the sources that support them. Turn on `enableCitations` when you create or resume a session, then read the `citations` payload on `assistant.message` events to render footnotes, source lists, or inline links. + +> [!WARNING] +> Citations are experimental. The option name, event payload, and provider coverage can change in a future release. + +## How citations work + +Citations are produced by the model provider, not by the SDK. The flow has three parts: + +1. Your application supplies citable material, such as a document attachment or a tool result that carries source content. +1. The runtime marks that material as citable on the wire when `enableCitations` is on. For Anthropic models, file attachments are sent as `document` blocks with citations enabled. +1. The model returns citation metadata, and the runtime normalizes it into a provider-agnostic `citations` object on the final `assistant.message` event. + +Provider support is limited. The `provider` field on each source records where the citation came from: + +| Provider value | Meaning | +|---|---| +| `anthropic` | Citation produced by an Anthropic (Claude) model response | +| `openai` | Citation produced by an OpenAI model response | +| `client` | Citation synthesized by the runtime from tool output | + +> [!NOTE] +> Turning on `enableCitations` does not guarantee that a response contains citations. Models emit them only when the response is grounded in citable source material. Always treat the `citations` field as optional. + +## Enable citations on a session + +Set the option on session create, and set it again on resume if you want citations after a restart. + +
+TypeScript + + + +```typescript +const session = await client.createSession({ + onPermissionRequest: approveAll, + enableCitations: true, +}); + +const resumed = await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + enableCitations: true, +}); +``` + +
+
+Python + + + +```python +session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_citations=True, +) + +resumed = await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + enable_citations=True, +) +``` + +
+
+Go + + + +```go +session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + EnableCitations: copilot.Bool(true), +}) + +resumed, err := client.ResumeSession(ctx, session.SessionID, &copilot.ResumeSessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + EnableCitations: copilot.Bool(true), +}) +``` + +
+
+.NET + + + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + OnPermissionRequest = PermissionHandler.ApproveAll, + EnableCitations = true, +}); + +var resumed = await client.ResumeSessionAsync(session.SessionId, new ResumeSessionConfig +{ + OnPermissionRequest = PermissionHandler.ApproveAll, + EnableCitations = true, +}); +``` + +
+
+Java + + + +```java +CopilotSession session = client + .createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setEnableCitations(true)) + .get(); + +CopilotSession resumed = client + .resumeSession(session.getSessionId(), new ResumeSessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setEnableCitations(true)) + .get(); +``` + +
+
+Rust + + + +```rust +let session = client + .create_session( + SessionConfig::new() + .approve_all_permissions() + .with_enable_citations(true), + ) + .await?; + +let resumed = client + .resume_session( + ResumeSessionConfig::new(session.id().clone()) + .approve_all_permissions() + .with_enable_citations(true), + ) + .await?; +``` + +
+ +## Read citations from assistant messages + +Citations arrive on the final `assistant.message` event, not on `assistant.message_delta` events. Wait for the final message before you render source markers. + +
+TypeScript + + + +```typescript +session.on((event) => { + if (event.type !== "assistant.message" || !event.data.citations) { + return; + } + + const { sources, spans } = event.data.citations; + const sourceById = new Map(sources.map((source) => [source.id, source])); + + for (const span of spans) { + const quoted = event.data.content.slice(span.startIndex, span.endIndex); + for (const reference of span.references) { + const source = sourceById.get(reference.sourceId); + const label = source?.title ?? source?.url ?? source?.path ?? source?.id; + console.log(`"${quoted}" — ${label}`); + } + } +}); +``` + +
+
+Python + + + +```python +from copilot.session_events import SessionEventType + +def utf16_slice(text: str, start: int, end: int) -> str: + """Slice by UTF-16 code units, which is how span offsets are measured.""" + units = text.encode("utf-16-le") + return units[start * 2 : end * 2].decode("utf-16-le") + +def handle(event): + if event.type != SessionEventType.ASSISTANT_MESSAGE or not event.data.citations: + return + + sources = {source.id: source for source in event.data.citations.sources} + + for span in event.data.citations.spans: + quoted = utf16_slice(event.data.content, span.start_index, span.end_index) + for reference in span.references: + source = sources[reference.source_id] + label = source.title or source.url or source.path or source.id + print(f'"{quoted}" — {label}') + +session.on(handle) +``` + +
+
+Go + + + +```go +// import "unicode/utf16" + +session.On(func(event copilot.SessionEvent) { + d, ok := event.Data.(*copilot.AssistantMessageData) + if !ok || d.Citations == nil { + return + } + + sources := map[string]copilot.CitationSource{} + for _, source := range d.Citations.Sources { + sources[source.ID] = source + } + + // Span offsets are UTF-16 code units, so index the UTF-16 view of the content. + units := utf16.Encode([]rune(d.Content)) + + for _, span := range d.Citations.Spans { + quoted := string(utf16.Decode(units[span.StartIndex:span.EndIndex])) + for _, reference := range span.References { + source := sources[reference.SourceID] + label := source.ID + switch { + case source.Title != nil: + label = *source.Title + case source.URL != nil: + label = *source.URL + case source.Path != nil: + label = *source.Path + } + fmt.Printf("%q — %s\n", quoted, label) + } + } +}) +``` + +
+
+.NET + + + +```csharp +session.On(evt => +{ + if (evt is not AssistantMessageEvent message || message.Data.Citations is null) + { + return; + } + + var sources = message.Data.Citations.Sources.ToDictionary(source => source.Id); + + foreach (var span in message.Data.Citations.Spans) + { + var quoted = message.Data.Content[(int)span.StartIndex..(int)span.EndIndex]; + foreach (var reference in span.References) + { + var source = sources[reference.SourceId]; + var label = source.Title ?? source.Url ?? source.Path ?? source.Id; + Console.WriteLine($"\"{quoted}\" — {label}"); + } + } +}); +``` + +
+
+Java + + + +```java +session.on(AssistantMessageEvent.class, event -> { + Citations citations = event.getData().citations(); + if (citations == null) { + return; + } + + Map sources = citations.sources().stream() + .collect(Collectors.toMap(CitationSource::id, source -> source)); + + for (CitationSpan span : citations.spans()) { + String quoted = event.getData().content() + .substring(span.startIndex().intValue(), span.endIndex().intValue()); + for (CitationReference reference : span.references()) { + CitationSource source = sources.get(reference.sourceId()); + String label = source.title() != null ? source.title() + : source.url() != null ? source.url() + : source.path() != null ? source.path() + : source.id(); + System.out.printf("\"%s\" — %s%n", quoted, label); + } + } +}); +``` + +
+
+Rust + + + +```rust +use github_copilot_sdk::session_events::AssistantMessageData; +use std::collections::HashMap; + +let mut events = session.subscribe(); + +while let Ok(event) = events.recv().await { + if event.event_type != "assistant.message" { + continue; + } + + let Some(data) = event.typed_data::() else { + continue; + }; + let Some(citations) = data.citations.as_ref() else { + continue; + }; + + let sources: HashMap<&str, _> = citations + .sources + .iter() + .map(|source| (source.id.as_str(), source)) + .collect(); + + // Span offsets are UTF-16 code units, so index the UTF-16 view of the content. + let units: Vec = data.content.encode_utf16().collect(); + + for span in &citations.spans { + let quoted = String::from_utf16_lossy( + &units[span.start_index as usize..span.end_index as usize], + ); + for reference in &span.references { + let Some(source) = sources.get(reference.source_id.as_str()) else { + continue; + }; + let label = source + .title + .as_deref() + .or(source.url.as_deref()) + .or(source.path.as_deref()) + .unwrap_or(source.id.as_str()); + println!("\"{quoted}\" — {label}"); + } + } +} +``` + +
+ +## Citation payload reference + +The `citations` object separates deduplicated sources from the spans that reference them, so a source cited five times appears once in `sources`. + +| Type | Field | Description | +|---|---|---| +| `Citations` | `sources` | Deduplicated set of sources referenced by the citation spans | +| `Citations` | `spans` | Spans of generated text annotated with their supporting sources | +| `CitationSource` | `id` | Stable, turn-scoped identifier referenced by `CitationReference.sourceId` | +| `CitationSource` | `provider` | System that produced the citation: `anthropic`, `openai`, or `client` | +| `CitationSource` | `title?` | Human-readable title of the source | +| `CitationSource` | `url?` | URL of the source, when it is a web resource | +| `CitationSource` | `path?` | File path relative to the agent workspace root, when the source is a file | +| `CitationSpan` | `startIndex` | Start offset in the final message content (UTF-16 code units, zero-based, inclusive) | +| `CitationSpan` | `endIndex` | End offset in the final message content (UTF-16 code units, zero-based, exclusive) | +| `CitationSpan` | `references` | The sources that support this span | +| `CitationReference` | `sourceId` | Identifier of the `CitationSource` this reference points to | +| `CitationReference` | `citedText?` | Exact text from the source that supports the span, when the model provides it | +| `CitationReference` | `location?` | Location within the source that supports the span | +| `CitationReference` | `providerMetadata?` | Provider-native correlation data, passed through opaquely | + +> [!TIP] +> Span offsets are measured in UTF-16 code units against the final `content` string. TypeScript, Java, and .NET strings are already UTF-16, so you can slice them directly. Python strings are indexed by Unicode code point and Go and Rust strings are UTF-8, so convert the content to UTF-16 code units before slicing, as the examples above do. + +### Citation locations + +`CitationReference.location` is a discriminated union keyed on `type`: + +| Location type | Fields | Use | +|---|---|---| +| `char` | `startIndex`, `endIndex` | Character range within the source text | +| `page` | `startPage`, `endPage` | Page range within a paginated document | +| `block` | `startBlock`, `endBlock` | Content-block range within a structured document | + +## Provide citable sources + +Citations need source material the model can attribute. There are two ways to supply it. + +### Attach documents to a message + +When citations are enabled and the session uses an Anthropic provider, file attachments are sent as `document` blocks with citations turned on, so the model can cite passages from them. + + + +```typescript +await session.sendAndWait({ + prompt: "Summarize the attached PDF and cite the passages you used.", + attachments: [ + { + type: "blob", + data: pdfBase64, + displayName: "quarterly-report.pdf", + mimeType: "application/pdf", + }, + ], +}); +``` + +See [Image input](./image-input.md) for the attachment API and the `file` and `blob` attachment shapes. + +### Return citable sources from a tool + +Tool results carry an experimental `citableSources` array. Each entry supplies `content` that the model can cite, along with an `id` and optional `title`, `url`, and `path`. These sources are persisted with the tool result, so they survive session resume, and citations built from them are tagged with the `client` provider. + +## Limitations + +* Citations are experimental in every SDK and are not covered by compatibility guarantees. +* Coverage depends on the model provider. A session configured for a provider without citation support emits no `citations` payload. +* Citations are only present on the final `assistant.message` event, so streaming consumers cannot render them mid-response. +* Public code and IP-duplication citations are not part of this surface. + +## Further reading + +* [Streaming events](./streaming-events.md): subscribe to session events and narrow event types +* [Image input](./image-input.md): attach files and in-memory blobs to a message +* [Session persistence](./session-persistence.md): resume sessions and re-apply session options +* [Compatibility](../troubleshooting/compatibility.md): SDK and CLI feature matrix From cc8c7f28ea9f1894fe91a2cf161e2e6f8a52ff51 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 5 Aug 2026 15:17:49 -0700 Subject: [PATCH 35/72] sdk: Expose disabled MCP servers across languages (#2260) * sdk: Expose disabled MCP servers across languages Allow callers to disable exact MCP server names per session on create and resume without mutating global settings. Cover all SDK wire mappings and verify plugin and built-in GitHub MCP servers stay stopped on the first message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * java: Apply Spotless formatting Format the disabled MCP server accessors and Javadocs exactly as required by the Java CI Spotless check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/features/mcp.md | 29 ++ dotnet/src/Client.cs | 4 + dotnet/src/Types.cs | 8 + dotnet/test/Unit/CloneTests.cs | 9 + dotnet/test/Unit/SerializationTests.cs | 4 + go/client.go | 6 + go/client_test.go | 52 +- go/types.go | 10 + .../github/copilot/SessionRequestBuilder.java | 2 + .../copilot/rpc/CreateSessionRequest.java | 15 + .../copilot/rpc/ResumeSessionConfig.java | 25 + .../copilot/rpc/ResumeSessionRequest.java | 15 + .../com/github/copilot/rpc/SessionConfig.java | 25 + .../com/github/copilot/ConfigCloneTest.java | 6 + .../copilot/SessionRequestBuilderTest.java | 16 +- nodejs/src/client.ts | 2 + nodejs/src/types.ts | 7 + nodejs/test/client.test.ts | 5 + .../test/e2e/disabled_mcp_servers.e2e.test.ts | 485 ++++++++++++++++++ python/copilot/client.py | 14 + python/test_client.py | 27 + rust/src/types.rs | 86 ++++ rust/src/wire.rs | 4 + test/harness/replayingCapiProxy.ts | 45 ++ test/harness/test-mcp-server.mjs | 14 +- 25 files changed, 907 insertions(+), 8 deletions(-) create mode 100644 nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 6f715bd2e..caac63327 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -154,6 +154,35 @@ await using var session = await client.CreateSessionAsync(new SessionConfig }); ``` +## Disabling configured servers per session + +Set `disabledMcpServers` to exact MCP server names that must not run in a session. +The setting is scoped to the individual create or resume request; it does not +modify global MCP settings or the server configuration. + +```typescript +const session = await client.createSession({ + mcpServers: { + filesystem: { type: "local", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "."] }, + github: { type: "http", url: "https://api.githubcopilot.com/mcp/" }, + }, + disabledMcpServers: ["github"], +}); +``` + +| SDK | Configuration property | +| --- | --- | +| Node.js | `disabledMcpServers` | +| Python | `disabled_mcp_servers` | +| Go | `DisabledMCPServers` | +| .NET | `DisabledMcpServers` | +| Java | `setDisabledMcpServers(...)` | +| Rust | `with_disabled_mcp_servers(...)` | + +On session creation and a **cold** resume, disabled servers are not started and +the runtime does not initiate their authentication. A resident resume cannot +undo a server that the runtime has already spawned. Names are matched exactly. + ## Tool configuration You can control which tools are available to an MCP server using the `tools` field. diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 4e271598e..03691fc63 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1189,6 +1189,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance Cloud: config.Cloud, InstructionDirectories: config.InstructionDirectories, PluginDirectories: config.PluginDirectories, + DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, Memory: config.Memory, @@ -1407,6 +1408,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ContinuePendingWork: config.ContinuePendingWork, InstructionDirectories: config.InstructionDirectories, PluginDirectories: config.PluginDirectories, + DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, Memory: config.Memory, @@ -2763,6 +2765,7 @@ internal record CreateSessionRequest( CloudSessionOptions? Cloud = null, IList? InstructionDirectories = null, IList? PluginDirectories = null, + [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, MemoryConfiguration? Memory = null, @@ -2872,6 +2875,7 @@ internal record ResumeSessionRequest( bool? ContinuePendingWork = null, IList? InstructionDirectories = null, IList? PluginDirectories = null, + [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, MemoryConfiguration? Memory = null, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index a6a1a8c4e..7489fbb4e 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3070,6 +3070,7 @@ protected SessionConfigBase(SessionConfigBase? other) DefaultAgent = other.DefaultAgent; Agent = other.Agent; DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; + DisabledMcpServers = other.DisabledMcpServers is not null ? [.. other.DisabledMcpServers] : null; EnableCitations = other.EnableCitations; EnableConfigDiscovery = other.EnableConfigDiscovery; SkipEmbeddingRetrieval = other.SkipEmbeddingRetrieval; @@ -3500,6 +3501,13 @@ protected SessionConfigBase(SessionConfigBase? other) /// List of skill names to disable. public IList? DisabledSkills { get; set; } + /// + /// Exact MCP server names to disable for this session. Disabled servers are not + /// started or authenticated on create or cold resume; a resident resume cannot + /// stop servers that are already running. + /// + public IList? DisabledMcpServers { get; set; } + /// /// Infinite session configuration for persistent workspaces and automatic compaction. /// When enabled (default), sessions automatically manage context limits and persist state. diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index ad8b0644e..88c653283 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -100,6 +100,7 @@ public void SessionConfig_Clone_CopiesAllProperties() SkillDirectories = ["/skills"], InstructionDirectories = ["/instructions"], DisabledSkills = ["skill1"], + DisabledMcpServers = ["server1"], PluginDirectories = ["/plugins"], LargeOutput = new LargeToolOutputConfig { Enabled = true, MaxSizeBytes = 2048, OutputDirectory = "/tmp/out" }, Memory = new MemoryConfiguration { Enabled = true }, @@ -140,6 +141,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Assert.Equal(original.SkillDirectories, clone.SkillDirectories); Assert.Equal(original.InstructionDirectories, clone.InstructionDirectories); Assert.Equal(original.DisabledSkills, clone.DisabledSkills); + Assert.Equal(original.DisabledMcpServers, clone.DisabledMcpServers); Assert.Equal(original.PluginDirectories, clone.PluginDirectories); Assert.Same(original.LargeOutput, clone.LargeOutput); Assert.Same(original.Memory, clone.Memory); @@ -162,6 +164,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() SkillDirectories = ["/skills"], InstructionDirectories = ["/instructions"], DisabledSkills = ["skill1"], + DisabledMcpServers = ["server1"], }; var clone = original.Clone(); @@ -176,6 +179,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() clone.SkillDirectories!.Add("/more"); clone.InstructionDirectories!.Add("/more-instructions"); clone.DisabledSkills!.Add("skill99"); + clone.DisabledMcpServers!.Add("server99"); // Original is unaffected Assert.Single(original.AvailableTools!); @@ -187,6 +191,7 @@ public void SessionConfig_Clone_CollectionsAreIndependent() Assert.Single(original.SkillDirectories!); Assert.Single(original.InstructionDirectories!); Assert.Single(original.DisabledSkills!); + Assert.Single(original.DisabledMcpServers!); } [Fact] @@ -214,6 +219,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() SkillDirectories = ["/skills"], InstructionDirectories = ["/instructions"], DisabledSkills = ["skill1"], + DisabledMcpServers = ["server1"], }; var clone = original.Clone(); @@ -228,6 +234,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() clone.SkillDirectories!.Add("/more"); clone.InstructionDirectories!.Add("/more-instructions"); clone.DisabledSkills!.Add("skill99"); + clone.DisabledMcpServers!.Add("server99"); // Original is unaffected Assert.Single(original.AvailableTools!); @@ -239,6 +246,7 @@ public void ResumeSessionConfig_Clone_CollectionsAreIndependent() Assert.Single(original.SkillDirectories!); Assert.Single(original.InstructionDirectories!); Assert.Single(original.DisabledSkills!); + Assert.Single(original.DisabledMcpServers!); } [Fact] @@ -299,6 +307,7 @@ public void Clone_WithNullCollections_ReturnsNullCollections() Assert.Null(clone.SkillDirectories); Assert.Null(clone.InstructionDirectories); Assert.Null(clone.DisabledSkills); + Assert.Null(clone.DisabledMcpServers); Assert.Null(clone.Tools); Assert.Null(clone.DefaultAgent); Assert.True(clone.IncludeSubAgentStreamingEvents); diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index acad44f19..9bccf3d77 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -411,12 +411,14 @@ public void SessionRequests_CanSerializePluginDirectoriesAndLargeOutput_WithSdkO createRequestType, ("SessionId", "session-id"), ("PluginDirectories", pluginDirs), + ("DisabledMcpServers", new List { "local-files", "remote-github" }), ("LargeOutput", largeOutput)); var createJson = JsonSerializer.Serialize(createRequest, createRequestType, options); using var createDocument = JsonDocument.Parse(createJson); var createRoot = createDocument.RootElement; Assert.Equal("/tmp/plugins/a", createRoot.GetProperty("pluginDirectories")[0].GetString()); + Assert.Equal("local-files", createRoot.GetProperty("disabledMcpServers")[0].GetString()); Assert.Equal("/tmp/plugins/b", createRoot.GetProperty("pluginDirectories")[1].GetString()); var createLargeOutput = createRoot.GetProperty("largeOutput"); Assert.True(createLargeOutput.GetProperty("enabled").GetBoolean()); @@ -428,12 +430,14 @@ public void SessionRequests_CanSerializePluginDirectoriesAndLargeOutput_WithSdkO resumeRequestType, ("SessionId", "session-id"), ("PluginDirectories", pluginDirs), + ("DisabledMcpServers", new List { "local-files", "remote-github" }), ("LargeOutput", largeOutput)); var resumeJson = JsonSerializer.Serialize(resumeRequest, resumeRequestType, options); using var resumeDocument = JsonDocument.Parse(resumeJson); var resumeRoot = resumeDocument.RootElement; Assert.Equal("/tmp/plugins/a", resumeRoot.GetProperty("pluginDirectories")[0].GetString()); + Assert.Equal("local-files", resumeRoot.GetProperty("disabledMcpServers")[0].GetString()); var resumeLargeOutput = resumeRoot.GetProperty("largeOutput"); Assert.True(resumeLargeOutput.GetProperty("enabled").GetBoolean()); Assert.Equal(1024, resumeLargeOutput.GetProperty("maxSizeBytes").GetInt64()); diff --git a/go/client.go b/go/client.go index eb9513030..d2c43c26b 100644 --- a/go/client.go +++ b/go/client.go @@ -814,6 +814,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.PluginDirectories = config.PluginDirectories req.InstructionDirectories = config.InstructionDirectories req.DisabledSkills = config.DisabledSkills + if config.DisabledMCPServers != nil { + req.DisabledMCPServers = &config.DisabledMCPServers + } req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch @@ -1193,6 +1196,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.PluginDirectories = config.PluginDirectories req.InstructionDirectories = config.InstructionDirectories req.DisabledSkills = config.DisabledSkills + if config.DisabledMCPServers != nil { + req.DisabledMCPServers = &config.DisabledMCPServers + } req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch diff --git a/go/client_test.go b/go/client_test.go index 14131bc4c..3cc123898 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -1103,9 +1103,11 @@ func TestSessionRequests_PluginDirectoriesAndLargeOutput(t *testing.T) { "outputDir": "/tmp/large-output", } expectedPluginDirs := []any{"/tmp/plugins/a", "/tmp/plugins/b"} + expectedDisabledMCPServers := []any{"local-files", "remote-github"} + disabledMCPServers := []string{"local-files", "remote-github"} t.Run("create includes pluginDirectories and largeOutput in JSON when set", func(t *testing.T) { - req := createSessionRequest{PluginDirectories: pluginDirs, LargeOutput: largeOutput} + req := createSessionRequest{PluginDirectories: pluginDirs, DisabledMCPServers: &disabledMCPServers, LargeOutput: largeOutput} data, err := json.Marshal(req) if err != nil { t.Fatalf("Failed to marshal: %v", err) @@ -1117,13 +1119,16 @@ func TestSessionRequests_PluginDirectoriesAndLargeOutput(t *testing.T) { if !reflect.DeepEqual(m["pluginDirectories"], expectedPluginDirs) { t.Errorf("Expected pluginDirectories %v, got %v", expectedPluginDirs, m["pluginDirectories"]) } + if !reflect.DeepEqual(m["disabledMcpServers"], expectedDisabledMCPServers) { + t.Errorf("Expected disabledMcpServers %v, got %v", expectedDisabledMCPServers, m["disabledMcpServers"]) + } if !reflect.DeepEqual(m["largeOutput"], expectedLargeOutput) { t.Errorf("Expected largeOutput %v, got %v", expectedLargeOutput, m["largeOutput"]) } }) t.Run("resume includes pluginDirectories and largeOutput in JSON when set", func(t *testing.T) { - req := resumeSessionRequest{SessionID: "s1", PluginDirectories: pluginDirs, LargeOutput: largeOutput} + req := resumeSessionRequest{SessionID: "s1", PluginDirectories: pluginDirs, DisabledMCPServers: &disabledMCPServers, LargeOutput: largeOutput} data, err := json.Marshal(req) if err != nil { t.Fatalf("Failed to marshal: %v", err) @@ -1135,11 +1140,36 @@ func TestSessionRequests_PluginDirectoriesAndLargeOutput(t *testing.T) { if !reflect.DeepEqual(m["pluginDirectories"], expectedPluginDirs) { t.Errorf("Expected pluginDirectories %v, got %v", expectedPluginDirs, m["pluginDirectories"]) } + if !reflect.DeepEqual(m["disabledMcpServers"], expectedDisabledMCPServers) { + t.Errorf("Expected disabledMcpServers %v, got %v", expectedDisabledMCPServers, m["disabledMcpServers"]) + } if !reflect.DeepEqual(m["largeOutput"], expectedLargeOutput) { t.Errorf("Expected largeOutput %v, got %v", expectedLargeOutput, m["largeOutput"]) } }) + t.Run("create and resume include explicit empty disabledMcpServers", func(t *testing.T) { + emptyDisabledMCPServers := []string{} + requests := []any{ + createSessionRequest{DisabledMCPServers: &emptyDisabledMCPServers}, + resumeSessionRequest{SessionID: "s1", DisabledMCPServers: &emptyDisabledMCPServers}, + } + + for _, request := range requests { + data, err := json.Marshal(request) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if value, ok := m["disabledMcpServers"]; !ok || !reflect.DeepEqual(value, []any{}) { + t.Errorf("Expected explicit empty disabledMcpServers, got %v", value) + } + } + }) + t.Run("create omits pluginDirectories and largeOutput when nil", func(t *testing.T) { req := createSessionRequest{} data, err := json.Marshal(req) @@ -1153,10 +1183,28 @@ func TestSessionRequests_PluginDirectoriesAndLargeOutput(t *testing.T) { if _, ok := m["pluginDirectories"]; ok { t.Errorf("Expected pluginDirectories to be omitted") } + if _, ok := m["disabledMcpServers"]; ok { + t.Error("Expected disabledMcpServers to be omitted") + } if _, ok := m["largeOutput"]; ok { t.Errorf("Expected largeOutput to be omitted") } }) + + t.Run("resume omits disabledMcpServers when nil", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1"} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if _, ok := m["disabledMcpServers"]; ok { + t.Error("Expected disabledMcpServers to be omitted") + } + }) } func TestSessionRequests_Memory(t *testing.T) { diff --git a/go/types.go b/go/types.go index 19689c707..e44d91acd 100644 --- a/go/types.go +++ b/go/types.go @@ -1376,6 +1376,10 @@ type SessionConfig struct { InstructionDirectories []string // DisabledSkills is a list of skill names to disable DisabledSkills []string + // DisabledMCPServers is a list of exact MCP server names to disable for this session. + // Disabled servers are not started or authenticated on create or cold resume. + // A resident resume cannot stop servers that are already running. + DisabledMCPServers []string // InfiniteSessions configures infinite sessions for persistent workspaces and automatic compaction. // When enabled (default), sessions automatically manage context limits and persist state. InfiniteSessions *InfiniteSessionConfig @@ -1855,6 +1859,10 @@ type ResumeSessionConfig struct { InstructionDirectories []string // DisabledSkills is a list of skill names to disable DisabledSkills []string + // DisabledMCPServers is a list of exact MCP server names to disable for this session. + // Disabled servers are not started or authenticated on create or cold resume. + // A resident resume cannot stop servers that are already running. + DisabledMCPServers []string // InfiniteSessions configures infinite sessions for persistent workspaces and automatic compaction. InfiniteSessions *InfiniteSessionConfig // LargeOutput configures handling of large tool outputs. When a tool produces @@ -2390,6 +2398,7 @@ type createSessionRequest struct { PluginDirectories []string `json:"pluginDirectories,omitempty"` InstructionDirectories []string `json:"instructionDirectories,omitempty"` DisabledSkills []string `json:"disabledSkills,omitempty"` + DisabledMCPServers *[]string `json:"disabledMcpServers,omitempty"` InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` @@ -2485,6 +2494,7 @@ type resumeSessionRequest struct { PluginDirectories []string `json:"pluginDirectories,omitempty"` InstructionDirectories []string `json:"instructionDirectories,omitempty"` DisabledSkills []string `json:"disabledSkills,omitempty"` + DisabledMCPServers *[]string `json:"disabledMcpServers,omitempty"` InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 62c712025..add4a79b6 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -160,6 +160,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setToolSearch(config.getToolSearch()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); + request.setDisabledMcpServers(config.getDisabledMcpServers()); request.setConfigDirectory(config.getConfigDirectory()); config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); config.getSkipEmbeddingRetrieval().ifPresent(request::setSkipEmbeddingRetrieval); @@ -309,6 +310,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setToolSearch(config.getToolSearch()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); + request.setDisabledMcpServers(config.getDisabledMcpServers()); request.setInfiniteSessions(config.getInfiniteSessions()); request.setModelCapabilities(config.getModelCapabilities()); diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 44cb8145c..46f59a28c 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -152,6 +152,9 @@ public final class CreateSessionRequest { @JsonProperty("disabledSkills") private List disabledSkills; + @JsonProperty("disabledMcpServers") + private List disabledMcpServers; + @JsonProperty("configDir") private String configDirectory; @@ -697,6 +700,18 @@ public void setDisabledSkills(List disabledSkills) { this.disabledSkills = disabledSkills; } + /** Gets disabled MCP server names. @return the server names */ + public List getDisabledMcpServers() { + return disabledMcpServers == null ? null : Collections.unmodifiableList(disabledMcpServers); + } + + /** + * Sets disabled MCP server names. @param disabledMcpServers the server names + */ + public void setDisabledMcpServers(List disabledMcpServers) { + this.disabledMcpServers = disabledMcpServers; + } + /** Gets config directory. @return the config directory path */ public String getConfigDirectory() { return configDirectory; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 05b73e88a..1e32ec847 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -94,6 +94,7 @@ public class ResumeSessionConfig { private ToolSearchConfig toolSearch; private MemoryConfiguration memory; private List disabledSkills; + private List disabledMcpServers; private InfiniteSessionConfig infiniteSessions; private Consumer onEvent; private List commands; @@ -1577,6 +1578,29 @@ public ResumeSessionConfig setDisabledSkills(List disabledSkills) { return this; } + /** + * Gets exact MCP server names disabled for this session. + * + * @return the disabled MCP server names, or {@code null} when none are disabled + */ + public List getDisabledMcpServers() { + return disabledMcpServers == null ? null : Collections.unmodifiableList(disabledMcpServers); + } + + /** + * Sets exact MCP server names to disable for this session. Disabled servers are + * not started or authenticated on create or cold resume; a resident resume + * cannot stop servers already running. + * + * @param disabledMcpServers + * the server names to disable + * @return this config for method chaining + */ + public ResumeSessionConfig setDisabledMcpServers(List disabledMcpServers) { + this.disabledMcpServers = disabledMcpServers; + return this; + } + /** * Gets the infinite session configuration. * @@ -1954,6 +1978,7 @@ public ResumeSessionConfig clone() { copy.toolSearch = this.toolSearch; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; + copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; copy.infiniteSessions = this.infiniteSessions; copy.onEvent = this.onEvent; copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index a0ecc2ed5..3fe17b182 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -192,6 +192,9 @@ public final class ResumeSessionRequest { @JsonProperty("disabledSkills") private List disabledSkills; + @JsonProperty("disabledMcpServers") + private List disabledMcpServers; + @JsonProperty("infiniteSessions") private InfiniteSessionConfig infiniteSessions; @@ -913,6 +916,18 @@ public void setDisabledSkills(List disabledSkills) { this.disabledSkills = disabledSkills; } + /** Gets disabled MCP server names. @return the server names */ + public List getDisabledMcpServers() { + return disabledMcpServers == null ? null : Collections.unmodifiableList(disabledMcpServers); + } + + /** + * Sets disabled MCP server names. @param disabledMcpServers the server names + */ + public void setDisabledMcpServers(List disabledMcpServers) { + this.disabledMcpServers = disabledMcpServers; + } + /** Gets infinite sessions config. @return the infinite sessions config */ public InfiniteSessionConfig getInfiniteSessions() { return infiniteSessions; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index e36a02793..ad62551fd 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -84,6 +84,7 @@ public class SessionConfig { private ToolSearchConfig toolSearch; private MemoryConfiguration memory; private List disabledSkills; + private List disabledMcpServers; private String configDirectory; private Boolean enableConfigDiscovery; private Boolean skipEmbeddingRetrieval; @@ -1268,6 +1269,29 @@ public SessionConfig setDisabledSkills(List disabledSkills) { return this; } + /** + * Gets exact MCP server names disabled for this session. + * + * @return the disabled MCP server names, or {@code null} when none are disabled + */ + public List getDisabledMcpServers() { + return disabledMcpServers == null ? null : Collections.unmodifiableList(disabledMcpServers); + } + + /** + * Sets exact MCP server names to disable for this session. Disabled servers are + * not started or authenticated on create or cold resume; a resident resume + * cannot stop servers already running. + * + * @param disabledMcpServers + * the server names to disable + * @return this config for method chaining + */ + public SessionConfig setDisabledMcpServers(List disabledMcpServers) { + this.disabledMcpServers = disabledMcpServers; + return this; + } + /** * Gets the custom configuration directory. * @@ -2079,6 +2103,7 @@ public SessionConfig clone() { copy.toolSearch = this.toolSearch; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; + copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; copy.configDirectory = this.configDirectory; copy.enableConfigDiscovery = this.enableConfigDiscovery; copy.skipEmbeddingRetrieval = this.skipEmbeddingRetrieval; diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index a8e7fb2e0..c3f726ca1 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -120,6 +120,7 @@ void sessionConfigCloneBasic() { original.setReasoningSummary("detailed"); original.setContextTier("long_context"); original.setPluginDirectories(List.of("/plugins/a", "/plugins/b")); + original.setDisabledMcpServers(List.of("local-files", "remote-github")); original.setLargeOutput( new LargeToolOutputConfig().setEnabled(true).setMaxSizeBytes(1024L).setOutputDirectory("/tmp/out")); original.setMemory(new MemoryConfiguration().setEnabled(true)); @@ -133,6 +134,7 @@ void sessionConfigCloneBasic() { assertEquals(original.getReasoningSummary(), cloned.getReasoningSummary()); assertEquals(original.getContextTier(), cloned.getContextTier()); assertEquals(original.getPluginDirectories(), cloned.getPluginDirectories()); + assertEquals(original.getDisabledMcpServers(), cloned.getDisabledMcpServers()); assertEquals(original.getLargeOutput(), cloned.getLargeOutput()); assertEquals(original.getMemory(), cloned.getMemory()); assertEquals(original.isStreaming(), cloned.isStreaming()); @@ -146,6 +148,7 @@ void sessionConfigListIndependence() { toolList.add("bash"); original.setAvailableTools(toolList); original.setInstructionDirectories(new ArrayList<>(List.of("/path/a", "/path/b"))); + original.setDisabledMcpServers(new ArrayList<>(List.of("local-files"))); SessionConfig cloned = original.clone(); @@ -156,6 +159,7 @@ void sessionConfigListIndependence() { assertEquals(2, cloned.getAvailableTools().size()); assertEquals(3, original.getAvailableTools().size()); assertEquals(List.of("/path/a", "/path/b"), cloned.getInstructionDirectories()); + assertEquals(List.of("local-files"), cloned.getDisabledMcpServers()); } @Test @@ -194,6 +198,7 @@ void resumeSessionConfigCloneBasic() { original.setReasoningSummary("none"); original.setContextTier("long_context"); original.setPluginDirectories(List.of("/plugins/r")); + original.setDisabledMcpServers(List.of("local-files-r")); original.setLargeOutput( new LargeToolOutputConfig().setEnabled(false).setMaxSizeBytes(2048L).setOutputDirectory("/tmp/resume")); original.setMemory(new MemoryConfiguration().setEnabled(false)); @@ -205,6 +210,7 @@ void resumeSessionConfigCloneBasic() { assertEquals(original.getReasoningSummary(), cloned.getReasoningSummary()); assertEquals(original.getContextTier(), cloned.getContextTier()); assertEquals(original.getPluginDirectories(), cloned.getPluginDirectories()); + assertEquals(original.getDisabledMcpServers(), cloned.getDisabledMcpServers()); assertEquals(original.getLargeOutput(), cloned.getLargeOutput()); assertEquals(original.getMemory(), cloned.getMemory()); assertEquals(original.isStreaming(), cloned.isStreaming()); diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 54773662c..329a7500a 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -169,13 +169,17 @@ void testBuildCreateRequestSetsContextTier() { } @Test - void testBuildCreateRequestSetsPluginDirectoriesAndLargeOutput() { + void testBuildCreateRequestSetsPluginDirectoriesAndLargeOutput() throws Exception { var largeOutput = new LargeToolOutputConfig().setEnabled(true).setMaxSizeBytes(1024L) .setOutputDirectory("/tmp/out"); - var config = new SessionConfig().setPluginDirectories(List.of("/plugins/a")).setLargeOutput(largeOutput); + var config = new SessionConfig().setPluginDirectories(List.of("/plugins/a")) + .setDisabledMcpServers(List.of("local-files", "remote-github")).setLargeOutput(largeOutput); CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); assertEquals(List.of("/plugins/a"), request.getPluginDirectories()); + assertEquals(List.of("local-files", "remote-github"), request.getDisabledMcpServers()); assertEquals(largeOutput, request.getLargeOutput()); + assertTrue(JsonRpcClient.getObjectMapper().writeValueAsString(request) + .contains("\"disabledMcpServers\":[\"local-files\",\"remote-github\"]")); } @Test @@ -460,13 +464,17 @@ void testBuildResumeRequestSetsContextTier() { } @Test - void testBuildResumeRequestSetsPluginDirectoriesAndLargeOutput() { + void testBuildResumeRequestSetsPluginDirectoriesAndLargeOutput() throws Exception { var largeOutput = new LargeToolOutputConfig().setEnabled(false).setMaxSizeBytes(2048L) .setOutputDirectory("/tmp/resume"); - var config = new ResumeSessionConfig().setPluginDirectories(List.of("/plugins/r")).setLargeOutput(largeOutput); + var config = new ResumeSessionConfig().setPluginDirectories(List.of("/plugins/r")) + .setDisabledMcpServers(List.of("local-files-r")).setLargeOutput(largeOutput); ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-16", config); assertEquals(List.of("/plugins/r"), request.getPluginDirectories()); + assertEquals(List.of("local-files-r"), request.getDisabledMcpServers()); assertEquals(largeOutput, request.getLargeOutput()); + assertTrue(JsonRpcClient.getObjectMapper().writeValueAsString(request) + .contains("\"disabledMcpServers\":[\"local-files-r\"]")); } @Test diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 3e100eddd..b4fe0f463 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1599,6 +1599,7 @@ export class CopilotClient { pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, + disabledMcpServers: config.disabledMcpServers, infiniteSessions: config.infiniteSessions, memory: config.memory, gitHubToken: config.gitHubToken, @@ -1842,6 +1843,7 @@ export class CopilotClient { pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, + disabledMcpServers: config.disabledMcpServers, infiniteSessions: config.infiniteSessions, memory: config.memory, disableResume: config.suppressResumeEvent, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index b3d572496..035ab6563 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2535,6 +2535,13 @@ export interface SessionConfigBase { */ disabledSkills?: string[]; + /** + * Exact MCP server names to disable for this session. Disabled servers are not + * started or authenticated when creating or cold-resuming a session. Supplying + * this on a resident resume cannot stop servers that are already running. + */ + disabledMcpServers?: string[]; + /** * Infinite session configuration for persistent workspaces and automatic compaction. * When enabled (default), sessions automatically manage context limits and persist state. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index bbe6fbe66..254a21fa4 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -1030,6 +1030,7 @@ describe("CopilotClient", () => { }); const pluginDirs = ["/tmp/plugins/a", "/tmp/plugins/b"]; + const disabledMcpServers = ["local-files", "remote-github"]; const largeOutput = { enabled: true, maxSizeBytes: 1024, @@ -1044,11 +1045,13 @@ describe("CopilotClient", () => { const session = await client.createSession({ onPermissionRequest: approveAll, pluginDirectories: pluginDirs, + disabledMcpServers, largeOutput, }); await client.resumeSession(session.sessionId, { onPermissionRequest: approveAll, pluginDirectories: pluginDirs, + disabledMcpServers, largeOutput, }); @@ -1059,8 +1062,10 @@ describe("CopilotClient", () => { ([method]) => method === "session.resume" )![1] as any; expect(createPayload.pluginDirectories).toEqual(pluginDirs); + expect(createPayload.disabledMcpServers).toEqual(disabledMcpServers); expect(createPayload.largeOutput).toEqual(expectedWireLargeOutput); expect(resumePayload.pluginDirectories).toEqual(pluginDirs); + expect(resumePayload.disabledMcpServers).toEqual(disabledMcpServers); expect(resumePayload.largeOutput).toEqual(expectedWireLargeOutput); }); diff --git a/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts b/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts new file mode 100644 index 000000000..ce1a504e8 --- /dev/null +++ b/nodejs/test/e2e/disabled_mcp_servers.e2e.test.ts @@ -0,0 +1,485 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { randomUUID } from "node:crypto"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; +import { + approveAll, + CopilotRequestHandler, + RuntimeConnection, + type CopilotSession, +} from "../../src/index.js"; +import { createSdkTestContext, isInProcessTransport } from "./harness/sdkTestContext.js"; +import { waitForCondition } from "./harness/sdkTestHelper.js"; + +const __dirname = resolve(fileURLToPath(new URL(".", import.meta.url))); +const TEST_MCP_SERVER = resolve(__dirname, "../../../test/harness/test-mcp-server.mjs"); +const SYNTHETIC_RESPONSE = "PERSISTED_SESSION_READY"; +const MCP_TRIGGER_PROMPT = "Reply with the configured MCP test completion marker."; + +class PersistingRequestHandler extends CopilotRequestHandler { + protected override async sendRequest(request: Request): Promise { + const body = request.body ? await request.text() : ""; + const wantsStream = /"stream"\s*:\s*true/.test(body); + const url = request.url.toLowerCase(); + + if (url.endsWith("/models")) { + return new Response(MODEL_CATALOG_JSON, { + status: 200, + headers: { "content-type": "application/json" }, + }); + } + + if (url.includes("/responses")) { + return new Response(wantsStream ? RESPONSE_STREAM : RESPONSE_JSON, { + status: 200, + headers: { + "content-type": wantsStream ? "text/event-stream" : "application/json", + }, + }); + } + + if (url.includes("/chat/completions")) { + return new Response( + wantsStream ? CHAT_COMPLETION_STREAM : CHAT_COMPLETION_RESPONSE_JSON, + { + status: 200, + headers: { + "content-type": wantsStream ? "text/event-stream" : "application/json", + }, + } + ); + } + + return new Response("{}", { + status: 200, + headers: { "content-type": "application/json" }, + }); + } +} + +const RESPONSE_STREAM = [ + { + event: "response.created", + data: { + type: "response.created", + response: { + id: "persisted-session", + object: "response", + status: "in_progress", + output: [], + }, + }, + }, + { + event: "response.output_item.added", + data: { + type: "response.output_item.added", + output_index: 0, + item: { id: "message-1", type: "message", role: "assistant", content: [] }, + }, + }, + { + event: "response.content_part.added", + data: { + type: "response.content_part.added", + output_index: 0, + content_index: 0, + part: { type: "output_text", text: "" }, + }, + }, + { + event: "response.output_text.delta", + data: { + type: "response.output_text.delta", + output_index: 0, + content_index: 0, + delta: SYNTHETIC_RESPONSE, + }, + }, + { + event: "response.output_text.done", + data: { + type: "response.output_text.done", + output_index: 0, + content_index: 0, + text: SYNTHETIC_RESPONSE, + }, + }, + { + event: "response.completed", + data: { + type: "response.completed", + response: { + id: "persisted-session", + object: "response", + status: "completed", + output: [ + { + id: "message-1", + type: "message", + role: "assistant", + content: [{ type: "output_text", text: SYNTHETIC_RESPONSE }], + }, + ], + usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 }, + }, + }, + }, +] + .map(({ event, data }) => `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`) + .join(""); + +const RESPONSE_JSON = JSON.stringify({ + id: "persisted-session", + object: "response", + status: "completed", + output: [ + { + id: "message-1", + type: "message", + role: "assistant", + content: [{ type: "output_text", text: SYNTHETIC_RESPONSE }], + }, + ], + usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 }, +}); + +const CHAT_COMPLETION_STREAM = [ + { + id: "persisted-session", + object: "chat.completion.chunk", + created: 1, + model: "claude-sonnet-4.5", + choices: [ + { + index: 0, + delta: { role: "assistant", content: SYNTHETIC_RESPONSE }, + finish_reason: null, + }, + ], + }, + { + id: "persisted-session", + object: "chat.completion.chunk", + created: 1, + model: "claude-sonnet-4.5", + choices: [{ index: 0, delta: {}, finish_reason: "stop" }], + }, +] + .map((data) => `data: ${JSON.stringify(data)}\n\n`) + .concat("data: [DONE]\n\n") + .join(""); + +const CHAT_COMPLETION_RESPONSE_JSON = JSON.stringify({ + id: "persisted-session", + object: "chat.completion", + created: 1, + model: "claude-sonnet-4.5", + choices: [ + { + index: 0, + message: { role: "assistant", content: SYNTHETIC_RESPONSE }, + finish_reason: "stop", + }, + ], + usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, +}); + +const MODEL_CATALOG_JSON = JSON.stringify({ + data: [ + { + id: "claude-sonnet-4.5", + name: "Claude Sonnet 4.5", + object: "model", + vendor: "Anthropic", + version: "1", + preview: false, + model_picker_enabled: true, + capabilities: { + type: "chat", + family: "claude-sonnet-4.5", + tokenizer: "o200k_base", + limits: { max_context_window_tokens: 200000, max_output_tokens: 8192 }, + supports: { streaming: true, tool_calls: true, parallel_tool_calls: true }, + }, + }, + ], +}); + +describe("disabled MCP servers", async () => { + const { + copilotClient: client, + createClient, + openAiEndpoint, + workDir, + } = await createSdkTestContext({ + copilotClientOptions: { + requestHandler: new PersistingRequestHandler(), + }, + }); + + function createPluginDirectory(prefix: string): { + pluginDirectory: string; + controlMarker: string; + disabledMarker: string; + } { + const pluginDirectory = join(workDir, `${prefix}-${randomUUID()}`); + mkdirSync(pluginDirectory, { recursive: true }); + const controlMarker = join(pluginDirectory, "control-started.log"); + const disabledMarker = join(pluginDirectory, "disabled-started.log"); + + writeFileSync( + join(pluginDirectory, "plugin.json"), + JSON.stringify({ + name: `${prefix}-${randomUUID()}`, + version: "1.0.0", + }) + ); + writeFileSync( + join(pluginDirectory, ".mcp.json"), + JSON.stringify({ + mcpServers: { + control: { + type: "stdio", + command: process.execPath, + args: [ + TEST_MCP_SERVER, + "--startup-marker", + controlMarker, + "--server-name", + "control", + ], + }, + disabled: { + type: "stdio", + command: process.execPath, + args: [ + TEST_MCP_SERVER, + "--startup-marker", + disabledMarker, + "--server-name", + "disabled", + ], + }, + }, + }) + ); + + return { pluginDirectory, controlMarker, disabledMarker }; + } + + function markerCount(markerPath: string): number { + if (!existsSync(markerPath)) { + return 0; + } + return readFileSync(markerPath, "utf8").trim().split("\n").filter(Boolean).length; + } + + async function waitForMarkerCount(markerPath: string, expectedCount: number): Promise { + await waitForCondition(() => markerCount(markerPath) >= expectedCount, { + timeoutMs: 60_000, + intervalMs: 100, + timeoutMessage: `Timed out waiting for ${markerPath} to be written ${expectedCount} time(s).`, + }); + } + + async function waitForMcpStatus( + session: CopilotSession, + serverName: string, + expectedStatus: string + ): Promise { + let lastStatus = ""; + await waitForCondition( + async () => { + const result = await session.rpc.mcp.list(); + const server = result.servers.find((candidate) => candidate.name === serverName); + lastStatus = server?.status ?? ""; + return lastStatus === expectedStatus; + }, + { + timeoutMs: 60_000, + intervalMs: 100, + timeoutMessage: `${serverName} did not reach ${expectedStatus}; last status was ${lastStatus}.`, + } + ); + } + + function expectSyntheticResponse(response: Awaited>) { + expect(response?.data.content).toBe(SYNTHETIC_RESPONSE); + } + + async function drainPostCreateRpc(session: CopilotSession): Promise { + // Drain a non-MCP post-create RPC without initializing MCP before the first model turn. + await session.rpc.metadata.snapshot(); + } + + async function mcpRequestCount(): Promise { + const requests = await openAiEndpoint.getRequests(); + return requests.filter((request) => request.method === "POST" && request.url === "/mcp") + .length; + } + + async function waitForMcpRequestCount(expectedCount: number): Promise { + let lastCount = 0; + await waitForCondition( + async () => { + lastCount = await mcpRequestCount(); + return lastCount >= expectedCount; + }, + { + timeoutMs: 60_000, + intervalMs: 100, + timeoutMessage: `Timed out waiting for ${expectedCount} /mcp request(s); saw ${lastCount}.`, + } + ); + } + + it( + "keeps disabled plugin MCP servers per-session on create", + { timeout: 120_000 }, + async () => { + const { + pluginDirectory: disabledPluginDirectory, + controlMarker: disabledControlMarker, + disabledMarker, + } = createPluginDirectory("disabled-mcp-create"); + + await using disabledSession = await client.createSession({ + onPermissionRequest: approveAll, + pluginDirectories: [disabledPluginDirectory], + disabledMcpServers: ["disabled"], + }); + + await drainPostCreateRpc(disabledSession); + expect(existsSync(disabledControlMarker)).toBe(false); + expect(existsSync(disabledMarker)).toBe(false); + expectSyntheticResponse( + await disabledSession.sendAndWait({ prompt: MCP_TRIGGER_PROMPT }) + ); + await waitForMarkerCount(disabledControlMarker, 1); + expect(existsSync(disabledMarker)).toBe(false); + await waitForMcpStatus(disabledSession, "control", "connected"); + await waitForMcpStatus(disabledSession, "disabled", "disabled"); + + const { + pluginDirectory: enabledPluginDirectory, + controlMarker: enabledControlMarker, + disabledMarker: enabledDisabledMarker, + } = createPluginDirectory("enabled-mcp-create"); + await using enabledSession = await client.createSession({ + onPermissionRequest: approveAll, + pluginDirectories: [enabledPluginDirectory], + }); + await drainPostCreateRpc(enabledSession); + expect(existsSync(enabledControlMarker)).toBe(false); + expect(existsSync(enabledDisabledMarker)).toBe(false); + expectSyntheticResponse( + await enabledSession.sendAndWait({ prompt: MCP_TRIGGER_PROMPT }) + ); + await waitForMarkerCount(enabledControlMarker, 1); + await waitForMarkerCount(enabledDisabledMarker, 1); + await waitForMcpStatus(enabledSession, "control", "connected"); + await waitForMcpStatus(enabledSession, "disabled", "connected"); + } + ); + + it( + "keeps the built-in GitHub MCP server disabled on the first message", + { timeout: 120_000 }, + async () => { + const disabledSession = await client.createSession({ + onPermissionRequest: approveAll, + enableConfigDiscovery: true, + enableMcpApps: true, + githubMcpToolConfig: { enableAllTools: true }, + disabledMcpServers: ["github-mcp-server"], + }); + + let disabledRequestsBeforeFirstMessage: number; + try { + await drainPostCreateRpc(disabledSession); + disabledRequestsBeforeFirstMessage = await mcpRequestCount(); + expect(disabledRequestsBeforeFirstMessage).toBe(0); + expectSyntheticResponse( + await disabledSession.sendAndWait({ prompt: MCP_TRIGGER_PROMPT }) + ); + expect(await mcpRequestCount()).toBe(disabledRequestsBeforeFirstMessage); + await waitForMcpStatus(disabledSession, "github-mcp-server", "disabled"); + expect(await mcpRequestCount()).toBe(disabledRequestsBeforeFirstMessage); + } finally { + await disabledSession.disconnect(); + } + + expect(await mcpRequestCount()).toBe(disabledRequestsBeforeFirstMessage); + + await using enabledSession = await client.createSession({ + onPermissionRequest: approveAll, + enableConfigDiscovery: true, + enableMcpApps: true, + githubMcpToolConfig: { enableAllTools: true }, + }); + await drainPostCreateRpc(enabledSession); + const requestsBeforeFirstMessage = await mcpRequestCount(); + expect(requestsBeforeFirstMessage).toBe(0); + expectSyntheticResponse( + await enabledSession.sendAndWait({ prompt: MCP_TRIGGER_PROMPT }) + ); + await waitForMcpRequestCount(requestsBeforeFirstMessage + 1); + await waitForMcpStatus(enabledSession, "github-mcp-server", "connected"); + } + ); + + it.skipIf(isInProcessTransport)( + "applies disabled plugin MCP servers on cold stdio resume", + async () => { + const { pluginDirectory, controlMarker, disabledMarker } = + createPluginDirectory("disabled-mcp-resume"); + const initialClient = createClient({ + connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), + requestHandler: new PersistingRequestHandler(), + }); + const resumeClient = createClient({ + connection: RuntimeConnection.forStdio({ path: process.env.COPILOT_CLI_PATH }), + }); + + try { + const originalSession = await initialClient.createSession({ + onPermissionRequest: approveAll, + enableSessionStore: true, + }); + const sessionId = originalSession.sessionId; + // A session.log entry alone does not materialize a session that a + // restarted runtime can resume. This self-contained model turn + // persists it without initializing MCP because no plugin directory + // is supplied until the resume request below. + const response = await originalSession.sendAndWait({ + prompt: "Return the configured persistence marker.", + }); + expectSyntheticResponse(response); + + expect(existsSync(controlMarker)).toBe(false); + expect(existsSync(disabledMarker)).toBe(false); + await initialClient.stop(); + + await using resumedSession = await resumeClient.resumeSession(sessionId, { + onPermissionRequest: approveAll, + enableSessionStore: true, + pluginDirectories: [pluginDirectory], + disabledMcpServers: ["disabled"], + }); + await waitForMcpStatus(resumedSession, "control", "connected"); + await waitForMcpStatus(resumedSession, "disabled", "disabled"); + await waitForMarkerCount(controlMarker, 1); + expect(existsSync(disabledMarker)).toBe(false); + } finally { + await initialClient.stop().catch(() => {}); + await resumeClient.stop().catch(() => {}); + } + } + ); +}); diff --git a/python/copilot/client.py b/python/copilot/client.py index 737619ef3..61f08e641 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2065,6 +2065,7 @@ async def create_session( plugin_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, + disabled_mcp_servers: list[str] | None = None, infinite_sessions: InfiniteSessionConfig | None = None, large_output: LargeToolOutputConfig | None = None, memory: MemoryConfiguration | None = None, @@ -2193,6 +2194,10 @@ async def create_session( instruction_directories: Additional directories to search for custom instruction files. disabled_skills: Skills to disable. + disabled_mcp_servers: Exact MCP server names to disable only for this + session. Disabled servers are not started or authenticated on + create or cold resume; a resident resume cannot stop servers + already running. This does not change global MCP settings. infinite_sessions: Infinite session configuration. memory: Session memory configuration. cloud: Creates a remote session in the cloud instead of a local @@ -2496,6 +2501,8 @@ async def create_session( # Add disabled skills configuration if provided if disabled_skills: payload["disabledSkills"] = disabled_skills + if disabled_mcp_servers is not None: + payload["disabledMcpServers"] = disabled_mcp_servers # Add infinite sessions configuration if provided if infinite_sessions: @@ -2764,6 +2771,7 @@ async def resume_session( plugin_directories: list[str] | None = None, instruction_directories: list[str] | None = None, disabled_skills: list[str] | None = None, + disabled_mcp_servers: list[str] | None = None, infinite_sessions: InfiniteSessionConfig | None = None, large_output: LargeToolOutputConfig | None = None, memory: MemoryConfiguration | None = None, @@ -2893,6 +2901,10 @@ async def resume_session( instruction_directories: Additional directories to search for custom instruction files. disabled_skills: Skills to disable. + disabled_mcp_servers: Exact MCP server names to disable only for this + session. Disabled servers are not started or authenticated on + create or cold resume; a resident resume cannot stop servers + already running. This does not change global MCP settings. infinite_sessions: Infinite session configuration. memory: Session memory configuration. on_event: Callback for session events. @@ -3165,6 +3177,8 @@ async def resume_session( payload["instructionDirectories"] = instruction_directories if disabled_skills: payload["disabledSkills"] = disabled_skills + if disabled_mcp_servers is not None: + payload["disabledMcpServers"] = disabled_mcp_servers if infinite_sessions: wire_config: dict[str, Any] = {} diff --git a/python/test_client.py b/python/test_client.py index 0bba1ccd7..9e116f449 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -937,6 +937,7 @@ async def mock_request(method, params, **kwargs): client._client.request = mock_request plugin_dirs = ["/tmp/plugins/a", "/tmp/plugins/b"] + disabled_mcp_servers = ["local-files", "remote-github"] large_output = { "enabled": True, "max_size_bytes": 1024, @@ -951,19 +952,45 @@ async def mock_request(method, params, **kwargs): session = await client.create_session( on_permission_request=PermissionHandler.approve_all, plugin_directories=plugin_dirs, + disabled_mcp_servers=disabled_mcp_servers, large_output=large_output, ) await client.resume_session( session.session_id, on_permission_request=PermissionHandler.approve_all, plugin_directories=plugin_dirs, + disabled_mcp_servers=disabled_mcp_servers, large_output=large_output, ) assert captured["session.create"]["pluginDirectories"] == plugin_dirs + assert captured["session.create"]["disabledMcpServers"] == disabled_mcp_servers assert captured["session.create"]["largeOutput"] == expected_large_output_wire assert captured["session.resume"]["pluginDirectories"] == plugin_dirs + assert captured["session.resume"]["disabledMcpServers"] == disabled_mcp_servers assert captured["session.resume"]["largeOutput"] == expected_large_output_wire + + empty_session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + disabled_mcp_servers=[], + ) + await client.resume_session( + empty_session.session_id, + on_permission_request=PermissionHandler.approve_all, + disabled_mcp_servers=[], + ) + assert captured["session.create"]["disabledMcpServers"] == [] + assert captured["session.resume"]["disabledMcpServers"] == [] + + omitted_session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + ) + await client.resume_session( + omitted_session.session_id, + on_permission_request=PermissionHandler.approve_all, + ) + assert "disabledMcpServers" not in captured["session.create"] + assert "disabledMcpServers" not in captured["session.resume"] finally: await client.force_stop() diff --git a/rust/src/types.rs b/rust/src/types.rs index 57497cb70..895529760 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1928,6 +1928,10 @@ pub struct SessionConfig { /// Skill names to disable. Skills in this set will not be available /// even if found in skill directories. pub disabled_skills: Option>, + /// Exact MCP server names to disable for this session. Disabled servers are + /// not started or authenticated on create or cold resume; a resident resume + /// cannot stop servers that are already running. + pub disabled_mcp_servers: Option>, /// Enable session hooks. When `true`, the CLI sends `hooks.invoke` /// RPC requests at key lifecycle points (pre/post tool use, prompt /// submission, session start/end, errors). @@ -2148,6 +2152,7 @@ impl std::fmt::Debug for SessionConfig { .field("large_output", &self.large_output) .field("tool_search", &self.tool_search) .field("disabled_skills", &self.disabled_skills) + .field("disabled_mcp_servers", &self.disabled_mcp_servers) .field("hooks", &self.hooks) .field("custom_agents", &self.custom_agents) .field("default_agent", &self.default_agent) @@ -2263,6 +2268,7 @@ impl Default for SessionConfig { large_output: None, tool_search: None, disabled_skills: None, + disabled_mcp_servers: None, hooks: None, custom_agents: None, default_agent: None, @@ -2425,6 +2431,7 @@ impl SessionConfig { large_output: self.large_output, tool_search: self.tool_search, disabled_skills: self.disabled_skills, + disabled_mcp_servers: self.disabled_mcp_servers, custom_agents: self.custom_agents, custom_agents_local_only: self.custom_agents_local_only, default_agent: self.default_agent, @@ -2862,6 +2869,16 @@ impl SessionConfig { self } + /// Set exact MCP server names to disable for this session. + pub fn with_disabled_mcp_servers(mut self, names: I) -> Self + where + I: IntoIterator, + S: Into, + { + self.disabled_mcp_servers = Some(names.into_iter().map(Into::into).collect()); + self + } + /// Set the custom agents (sub-agents) configured for this session. pub fn with_custom_agents>( mut self, @@ -3176,6 +3193,9 @@ pub struct ResumeSessionConfig { pub tool_search: Option, /// Skill names to disable on resume. pub disabled_skills: Option>, + /// Exact MCP server names to disable on resume. This prevents startup and + /// authentication during a cold resume, but cannot stop resident servers. + pub disabled_mcp_servers: Option>, /// Enable session hooks on resume. pub hooks: Option, /// Custom agents to re-supply on resume. @@ -3363,6 +3383,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("large_output", &self.large_output) .field("tool_search", &self.tool_search) .field("disabled_skills", &self.disabled_skills) + .field("disabled_mcp_servers", &self.disabled_mcp_servers) .field("hooks", &self.hooks) .field("custom_agents", &self.custom_agents) .field("default_agent", &self.default_agent) @@ -3522,6 +3543,7 @@ impl ResumeSessionConfig { large_output: self.large_output, tool_search: self.tool_search, disabled_skills: self.disabled_skills, + disabled_mcp_servers: self.disabled_mcp_servers, custom_agents: self.custom_agents, custom_agents_local_only: self.custom_agents_local_only, default_agent: self.default_agent, @@ -3616,6 +3638,7 @@ impl ResumeSessionConfig { large_output: None, tool_search: None, disabled_skills: None, + disabled_mcp_servers: None, hooks: None, custom_agents: None, default_agent: None, @@ -4032,6 +4055,16 @@ impl ResumeSessionConfig { self } + /// Set exact MCP server names to disable for this session. + pub fn with_disabled_mcp_servers(mut self, names: I) -> Self + where + I: IntoIterator, + S: Into, + { + self.disabled_mcp_servers = Some(names.into_iter().map(Into::into).collect()); + self + } + /// Re-supply custom agents on resume. pub fn with_custom_agents>( mut self, @@ -6342,6 +6375,10 @@ mod tests { let cfg = SessionConfig { plugin_directories: Some(vec![PathBuf::from("/tmp/plugins")]), + disabled_mcp_servers: Some(vec![ + "local-files".to_string(), + "remote-github".to_string(), + ]), large_output: Some( LargeToolOutputConfig::new() .with_enabled(true) @@ -6356,6 +6393,10 @@ mod tests { .expect("no duplicate handlers"); let wire_json = serde_json::to_value(&wire).unwrap(); assert_eq!(wire_json["pluginDirectories"][0], "/tmp/plugins"); + assert_eq!( + wire_json["disabledMcpServers"], + serde_json::json!(["local-files", "remote-github"]) + ); assert_eq!(wire_json["largeOutput"]["enabled"], true); assert_eq!(wire_json["largeOutput"]["maxSizeBytes"], 1024); assert_eq!(wire_json["largeOutput"]["outputDir"], "/tmp/large-output"); @@ -6365,6 +6406,7 @@ mod tests { .expect("default has no duplicate handlers"); let empty_json = serde_json::to_value(&empty_wire).unwrap(); assert!(empty_json.get("pluginDirectories").is_none()); + assert!(empty_json.get("disabledMcpServers").is_none()); assert!(empty_json.get("largeOutput").is_none()); } @@ -6414,6 +6456,7 @@ mod tests { let mut cfg = ResumeSessionConfig::new(SessionId::from("sess-1")); cfg.plugin_directories = Some(vec![PathBuf::from("/tmp/plugins-r")]); + cfg.disabled_mcp_servers = Some(vec!["local-files-r".to_string()]); cfg.large_output = Some( LargeToolOutputConfig::new() .with_enabled(false) @@ -6424,6 +6467,10 @@ mod tests { let (wire, _) = cfg.into_wire().expect("no duplicate handlers"); let wire_json = serde_json::to_value(&wire).unwrap(); assert_eq!(wire_json["pluginDirectories"][0], "/tmp/plugins-r"); + assert_eq!( + wire_json["disabledMcpServers"], + serde_json::json!(["local-files-r"]) + ); assert_eq!(wire_json["largeOutput"]["enabled"], false); assert_eq!(wire_json["largeOutput"]["maxSizeBytes"], 2048); assert_eq!(wire_json["largeOutput"]["outputDir"], "/tmp/large-output-r"); @@ -6433,9 +6480,38 @@ mod tests { .expect("default resume has no duplicate handlers"); let empty_json = serde_json::to_value(&empty_wire).unwrap(); assert!(empty_json.get("pluginDirectories").is_none()); + assert!(empty_json.get("disabledMcpServers").is_none()); assert!(empty_json.get("largeOutput").is_none()); } + #[test] + fn session_config_clones_disabled_mcp_servers() { + let create = SessionConfig::default().with_disabled_mcp_servers(["local-files"]); + let mut create_clone = create.clone(); + create_clone + .disabled_mcp_servers + .as_mut() + .expect("configured disabled MCP servers") + .push("remote-github".to_string()); + assert_eq!( + create.disabled_mcp_servers.as_deref(), + Some(&["local-files".to_string()][..]) + ); + + let resume = ResumeSessionConfig::new(SessionId::from("sess-1")) + .with_disabled_mcp_servers(["local-files"]); + let mut resume_clone = resume.clone(); + resume_clone + .disabled_mcp_servers + .as_mut() + .expect("configured disabled MCP servers") + .push("remote-github".to_string()); + assert_eq!( + resume.disabled_mcp_servers.as_deref(), + Some(&["local-files".to_string()][..]) + ); + } + #[test] fn session_config_builder_composes() { use indexmap::IndexMap; @@ -6457,6 +6533,7 @@ mod tests { .with_enable_on_demand_instruction_discovery(true) .with_skill_directories([PathBuf::from("/tmp/skills")]) .with_disabled_skills(["broken-skill"]) + .with_disabled_mcp_servers(["local-files"]) .with_agent("researcher") .with_config_directory(PathBuf::from("/tmp/config")) .with_working_directory(PathBuf::from("/tmp/work")) @@ -6495,6 +6572,10 @@ mod tests { cfg.disabled_skills.as_deref(), Some(&["broken-skill".to_string()][..]) ); + assert_eq!( + cfg.disabled_mcp_servers.as_deref(), + Some(&["local-files".to_string()][..]) + ); assert_eq!(cfg.agent.as_deref(), Some("researcher")); assert_eq!(cfg.config_directory, Some(PathBuf::from("/tmp/config"))); assert_eq!(cfg.working_directory, Some(PathBuf::from("/tmp/work"))); @@ -6533,6 +6614,7 @@ mod tests { .with_enable_on_demand_instruction_discovery(false) .with_skill_directories([PathBuf::from("/tmp/skills")]) .with_disabled_skills(["broken-skill"]) + .with_disabled_mcp_servers(["local-files"]) .with_agent("researcher") .with_config_directory(PathBuf::from("/tmp/config")) .with_working_directory(PathBuf::from("/tmp/work")) @@ -6571,6 +6653,10 @@ mod tests { cfg.disabled_skills.as_deref(), Some(&["broken-skill".to_string()][..]) ); + assert_eq!( + cfg.disabled_mcp_servers.as_deref(), + Some(&["local-files".to_string()][..]) + ); assert_eq!(cfg.agent.as_deref(), Some("researcher")); assert_eq!(cfg.config_directory, Some(PathBuf::from("/tmp/config"))); assert_eq!(cfg.working_directory, Some(PathBuf::from("/tmp/work"))); diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 261350e97..3e19063fc 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -130,6 +130,8 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub disabled_mcp_servers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub custom_agents: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub custom_agents_local_only: Option, @@ -274,6 +276,8 @@ pub(crate) struct SessionResumeWire { #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, #[serde(skip_serializing_if = "Option::is_none")] + pub disabled_mcp_servers: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub custom_agents: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub custom_agents_local_only: Option, diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 47ebda9f7..4c1be59f2 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -416,6 +416,51 @@ export class ReplayingCapiProxy extends CapturingHttpProxy { return; } + // Keep GitHub MCP tests hermetic while still capturing the request at + // the CAPI proxy. The tests only need a successful transport handshake; + // no fake tools are exposed. + if (options.requestOptions.path === "/mcp") { + if (options.requestOptions.method !== "POST") { + options.onResponseStart(200, commonResponseHeaders); + options.onResponseEnd(); + return; + } + + const request = JSON.parse(options.body ?? "{}") as { + id?: string | number; + method?: string; + params?: { protocolVersion?: string }; + }; + if (request.id === undefined) { + options.onResponseStart(202, commonResponseHeaders); + options.onResponseEnd(); + return; + } + + const result = + request.method === "initialize" + ? { + protocolVersion: + request.params?.protocolVersion ?? "2025-03-26", + capabilities: { tools: {} }, + serverInfo: { name: "e2e-github-mcp", version: "1.0.0" }, + } + : request.method === "tools/list" + ? { tools: [] } + : {}; + options.onResponseStart(200, { + "content-type": "application/json", + ...commonResponseHeaders, + }); + options.onData( + Buffer.from( + JSON.stringify({ jsonrpc: "2.0", id: request.id, result }), + ), + ); + options.onResponseEnd(); + return; + } + // Handle memory endpoints - return stub responses in tests // Matches: /agents/*/memory/*/enabled, /agents/*/memory/*/recent, etc. if (options.requestOptions.path?.match(/\/agents\/.*\/memory\//)) { diff --git a/test/harness/test-mcp-server.mjs b/test/harness/test-mcp-server.mjs index b2b32606d..a3a84b42b 100644 --- a/test/harness/test-mcp-server.mjs +++ b/test/harness/test-mcp-server.mjs @@ -13,9 +13,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { appendFile } from "node:fs/promises"; import { z } from "zod"; -const server = new McpServer({ name: "env-echo", version: "1.0.0" }); +function getArgument(name) { + const index = process.argv.indexOf(name); + return index === -1 ? undefined : process.argv[index + 1]; +} + +const startupMarkerPath = getArgument("--startup-marker"); +const serverName = getArgument("--server-name") ?? "env-echo"; +const server = new McpServer({ name: serverName, version: "1.0.0" }); server.tool( "get_env", @@ -27,5 +35,7 @@ server.tool( ); const transport = new StdioServerTransport(); +if (startupMarkerPath) { + await appendFile(startupMarkerPath, `${serverName}\n`); +} await server.connect(transport); - From 303de0ede41a34e3d026b962f2fca04e53f1f008 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 6 Aug 2026 00:44:25 +0000 Subject: [PATCH 36/72] docs: update version references to 1.0.9 --- java/README.md | 8 ++++---- java/jbang-example.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/README.md b/java/README.md index c3428eee0..94f2de410 100644 --- a/java/README.md +++ b/java/README.md @@ -32,14 +32,14 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. com.github copilot-sdk-java - 1.0.9-preview.3 + 1.0.9 ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.9-preview.3' +implementation 'com.github:copilot-sdk-java:1.0.9' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.10-preview.3-SNAPSHOT + 1.0.10-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.10-preview.3-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.10-SNAPSHOT' ``` ## Quick Start diff --git a/java/jbang-example.java b/java/jbang-example.java index 9abc39edd..bea647ae9 100644 --- a/java/jbang-example.java +++ b/java/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.9-preview.3 +//DEPS com.github:copilot-sdk-java:1.0.9 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From 05be4c0c51b5b64705afa3eea72a6850bc7fdf40 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 6 Aug 2026 00:44:52 +0000 Subject: [PATCH 37/72] [maven-release-plugin] prepare release java/v1.0.9 --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index efb54bbef..efdc43e7e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.10-preview.3-SNAPSHOT + 1.0.9 jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.9 From 781021e791a5c6404a81b09bc9932aabb5bc96b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 6 Aug 2026 00:44:55 +0000 Subject: [PATCH 38/72] [maven-release-plugin] prepare for next development iteration --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index efdc43e7e..fcdd9cc21 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.9 + 1.0.10-SNAPSHOT jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.9 + HEAD From a7a1d07166db33e778f6580c1671c8b3ac1c597b Mon Sep 17 00:00:00 2001 From: examon <1676292+examon@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:03:10 +0200 Subject: [PATCH 39/72] docs: correct the Python Customize Mode section IDs and action list (#2264) The Python README's "Customize Mode" section listed 10 section IDs and described "four string actions". The shipped Python API exposes 12 sections (`SystemMessageSection`) and 5 string actions (the literal arm of `SectionOverrideAction`): `preamble`, `runtime_instructions` and `preserve` were missing, so following the README it was impossible to discover them. Bring the two lines in line with the shipped API and with the equivalent sections in the Node.js, Go and .NET READMEs and in docs/getting-started.md, which already document all 12 sections and all 5 actions, including the section-group semantics that make `preamble` and `preserve` meaningful. Co-authored-by: examon --- python/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/README.md b/python/README.md index 10630fb84..bb17f68cc 100644 --- a/python/README.md +++ b/python/README.md @@ -710,9 +710,9 @@ async with await client.create_session( ... ``` -Available section IDs: `"identity"`, `"tone"`, `"tool_efficiency"`, `"environment_context"`, `"code_change_rules"`, `"guidelines"`, `"safety"`, `"tool_instructions"`, `"custom_instructions"`, `"last_instructions"`. +Available section IDs: `"preamble"`, `"identity"`, `"tone"`, `"tool_efficiency"`, `"environment_context"`, `"code_change_rules"`, `"guidelines"`, `"safety"`, `"tool_instructions"`, `"custom_instructions"`, `"runtime_instructions"`, `"last_instructions"`. `"identity"` and `"tool_instructions"` are section groups that target a collection of related sub-sections as a unit; use `"preamble"` to target just the identity preamble. -Each section override supports four string actions: `"replace"`, `"remove"`, `"append"`, and `"prepend"`. Unknown section IDs are handled gracefully: content is appended to additional instructions, and `"remove"` overrides are silently ignored. +Each section override supports five string actions: `"replace"`, `"remove"`, `"append"`, `"prepend"`, and `"preserve"` (a no-op that opts an individually-addressable section out of a group-level `"remove"`). Unknown section IDs are handled gracefully: content from `"replace"`/`"append"`/`"prepend"` overrides is appended to additional instructions, and `"remove"` overrides are silently ignored. You can also pass a transform callback as the `action` instead of a string. The callback receives the current section content and returns the new content (sync or async): From 9c91ad850cf64e225630204fcf222a8c44813a59 Mon Sep 17 00:00:00 2001 From: Rince Yuan Date: Thu, 6 Aug 2026 21:13:08 +0800 Subject: [PATCH 40/72] fix(java): preserve MCP permission extension data (#2276) * fix(java): preserve MCP permission extension data * test(java): preserve absent permission extension data Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * style(java): format permission regression test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: j-zhangyiyuan Co-authored-by: Steve Sanderson Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../github/copilot/rpc/PermissionRequest.java | 10 +++++++++ .../copilot/DataObjectCoverageTest.java | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java b/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java index a3297bfb2..fc49332b8 100644 --- a/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java @@ -5,8 +5,10 @@ package com.github.copilot.rpc; import java.io.IOException; +import java.util.LinkedHashMap; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -45,6 +47,14 @@ public class PermissionRequest { private Map extensionData; + @JsonAnySetter + private void setExtensionDataEntry(String key, Object value) { + if (extensionData == null) { + extensionData = new LinkedHashMap<>(); + } + extensionData.put(key, value); + } + private static final class ManagedApprovalRequiredDeserializer extends JsonDeserializer { @Override diff --git a/java/src/test/java/com/github/copilot/DataObjectCoverageTest.java b/java/src/test/java/com/github/copilot/DataObjectCoverageTest.java index f38a03836..f95c5bcc5 100644 --- a/java/src/test/java/com/github/copilot/DataObjectCoverageTest.java +++ b/java/src/test/java/com/github/copilot/DataObjectCoverageTest.java @@ -139,6 +139,27 @@ void permissionRequestSetExtensionData() { assertEquals("value", req.getExtensionData().get("key")); } + @Test + void permissionRequestPreservesMcpExtensionData() { + var request = PermissionRequest.fromJsonValue( + java.util.Map.of("kind", "mcp", "serverName", "playwright", "toolName", "playwright-browser_navigate", + "args", java.util.Map.of("url", "http://127.0.0.1:8106/docs/target-app/"))); + + assertEquals("mcp", request.getKind()); + assertEquals("playwright", request.getExtensionData().get("serverName")); + assertEquals("playwright-browser_navigate", request.getExtensionData().get("toolName")); + @SuppressWarnings("unchecked") + var args = (java.util.Map) request.getExtensionData().get("args"); + assertEquals("http://127.0.0.1:8106/docs/target-app/", args.get("url")); + } + + @Test + void permissionRequestWithoutExtensionDataPreservesNull() { + var request = PermissionRequest.fromJsonValue(java.util.Map.of("kind", "read", "toolCallId", "tool-123")); + + assertNull(request.getExtensionData()); + } + // ===== SectionOverride setContent ===== @Test From 4e95deee58f2712a9e75e66a1ffba4460cc9a1d5 Mon Sep 17 00:00:00 2001 From: examon <1676292+examon@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:34:27 +0200 Subject: [PATCH 41/72] Add `history.clearContext` and `Tool.isTerminal` across all SDKs (#2129) * Add history.clearContext and Tool.isTerminal across all SDKs Regenerates the RPC clients for the new `session.history.clearContext` method and the `session.context_cleared` event, and adds a hand-authored `isTerminal` tool flag to every language surface. `isTerminal` lets a tool declare that a successful call ends the agent turn: the runtime's tool phase halts instead of feeding the result back to the model for another round. A failed call leaves the loop running so the model can read the error and retry. Without it a turn-ending tool can only approximate the behavior by returning a rejected result, which halts the loop but is semantically wrong. Per language: - Node.js: `Tool.isTerminal`, `defineTool` config, both session-config serialization sites. - Go: `Tool.IsTerminal` with `json:"isTerminal,omitempty"`. - Python: `Tool.is_terminal`, `define_tool` overloads, both client serialization sites. - Rust: `Tool::is_terminal`, skipped when false. - Java: `ToolDefinition.isTerminal` as a record component, plus a seven-argument convenience constructor so existing call sites keep compiling. - .NET: `CopilotToolOptions.IsTerminal`, the `is_terminal` additional-property key, and the wire `ToolDefinition`. Adds serialization tests in Go, Rust and Java covering both the camelCase wire name and omission when unset; the Java test also pins the seven-argument constructor so the record change stays source-compatible. * Preserve isTerminal in Java fluent copies, add Node isTerminal wire tests Resolves the rebase onto main where both sides added an eighth tool option: main added metadata and this branch added isTerminal. - Java ToolDefinition now carries metadata and isTerminal as separate record components, keeps the seven- and eight-argument convenience constructors, and threads isTerminal through every fluent copy method so it is no longer dropped by .metadata()/.defer()/etc. - Adds ToolDefinition.isTerminal(boolean) so lambda-defined tools can set it, matching the other flags. - Adds the missing Node regression tests asserting isTerminal is forwarded on both session.create and session.resume, and omitted when unset. - Applies the repo rust formatter to the new is_terminal test. * Regenerate clearContext bindings for the tightened runtime contract Mirrors github/copilot-agent-runtime#14002 after review: - `HistoryClearContextRequest.prompt` is now required. A cleared window holding only system and developer messages is not a conversation a model can answer, so every clear seeds the window it creates. - `HistoryClearContextResult` loses the `cleared` discriminator. The RPC now rejects the cases it was meant to describe - a remote session, or a call made while no tool call is in flight - so there is one error channel instead of a success flag plus an error channel. - `ContextClearedData.prependMessages` is gone. It had no producer, and it was a permanent commitment on a durable event for a code path nothing exercised. Regenerated with `scripts/codegen`; only the clear-context hunks are taken, so unrelated schema drift stays out of this PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Close the two remaining reviewer findings on isTerminal The automated reviewer raised two suppressed findings against the new Tool.isTerminal field. Both were accurate. Rust: Tool has a hand-written Debug impl that enumerates every other serializable field, so is_terminal was silently missing from its output and a terminal tool debug-printed identically to a plain one. Add the field in declaration order, plus a test that fails if the hand-written impl drifts again. Python: dotnet, go, java, nodejs and rust all assert that isTerminal reaches the wire on both the session.create and session.resume paths and is omitted at its default. Python was the only SDK without that coverage. Add the test, mirroring the adjacent tool-metadata test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix @since on the new Java isTerminal setter ToolDefinition.isTerminal(boolean) is introduced by this PR but was tagged @since 1.0.7, a version released long ago in which the method did not exist, so the generated javadoc would misstate its availability. 1.0.11 is the current unreleased version: the pom is at 1.0.10-preview.3-SNAPSHOT, and the eight java/src/main files carrying @since 1.0.11 include ToolDefinition.createOverride in this same file. This was the only @since tag the PR added. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Expose isTerminal through Java tool annotations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Test .NET terminal tools on session requests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Cover terminal tool runtime behavior end to end Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document context clearing and terminal tools Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Normalize clear context snapshot formatting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: examon Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Steve Sanderson --- docs/features/README.md | 1 + docs/features/context-management.md | 57 ++++++++++++++++ docs/troubleshooting/compatibility.md | 1 + dotnet/src/Client.cs | 7 +- dotnet/src/CopilotTool.cs | 20 +++++- .../test/Unit/ClientSessionLifetimeTests.cs | 33 +++++++++ dotnet/test/Unit/CopilotToolTests.cs | 22 ++++++ go/client_test.go | 37 ++++++++++ go/types.go | 5 ++ .../github/copilot/rpc/ToolDefinition.java | 68 ++++++++++++++++--- .../com/github/copilot/tool/CopilotTool.java | 3 + .../copilot/tool/CopilotToolProcessor.java | 5 +- .../rpc/ToolDefinitionIsTerminalTest.java | 51 ++++++++++++++ .../tool/CopilotToolProcessorTest.java | 36 ++++++++-- nodejs/src/client.ts | 2 + nodejs/src/types.ts | 12 ++++ nodejs/test/client.test.ts | 62 +++++++++++++++++ nodejs/test/e2e/tool_results.e2e.test.ts | 2 + nodejs/test/e2e/tools.e2e.test.ts | 42 +++++++++++- python/copilot/client.py | 4 ++ python/copilot/tools.py | 15 ++++ python/test_client.py | 39 +++++++++++ rust/src/types.rs | 66 ++++++++++++++++++ ...minal_tool_and_starts_the_seeded_turn.yaml | 22 ++++++ 24 files changed, 592 insertions(+), 20 deletions(-) create mode 100644 docs/features/context-management.md create mode 100644 java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java create mode 100644 test/snapshots/tools/clears_context_from_a_terminal_tool_and_starts_the_seeded_turn.yaml diff --git a/docs/features/README.md b/docs/features/README.md index 9f3b937c9..f97140b78 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -21,6 +21,7 @@ These guides cover the capabilities you can add to your Copilot SDK application. | [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) | | [Usage and Billing](./usage-and-billing.md) | Read token counts, context-window utilization, AI credit cost, and account quota | | [Steering & Queueing](./steering-and-queueing.md) | Control message delivery—immediate steering vs. sequential queueing | +| [Context Clearing](./context-management.md) | Replace conversation context safely with terminal tools | | [Session Persistence](./session-persistence.md) | Resume sessions across restarts, manage session storage | | [Remote Sessions](./remote-sessions.md) | Share locally hosted sessions to GitHub web and mobile via Mission Control | | [Cloud Sessions](./cloud-sessions.md) | Run sessions on GitHub-hosted compute through Mission Control | diff --git a/docs/features/context-management.md b/docs/features/context-management.md new file mode 100644 index 000000000..6472b046a --- /dev/null +++ b/docs/features/context-management.md @@ -0,0 +1,57 @@ +# Context clearing and terminal tools + +Use `session.history.clearContext` when a host needs to replace the current conversation context without replacing the session. Typical uses include handoffs and host-managed context lifecycle policies. + +Context clearing is different from creating a new session: it preserves the session identity, system and developer messages, configuration, and event log while removing the model-facing conversation. + +> [!IMPORTANT] +> `clearContext` is a tool-handler primitive. The runtime rejects calls made without a tool call in flight, calls with an empty seed prompt, and calls on remote sessions. + +## Define a context-clearing tool + +A successful context-clearing tool should be terminal. Otherwise, the agent loop may make another model call against the newly cleared window before starting the seeded turn. + +```typescript +import { approveAll, CopilotClient, defineTool } from "@github/copilot-sdk"; +import type { CopilotSession } from "@github/copilot-sdk"; +import { z } from "zod"; + +const client = new CopilotClient(); +let session: CopilotSession; + +session = await client.createSession({ + onPermissionRequest: approveAll, + tools: [ + defineTool("clear_context", { + description: "Clear the conversation and start a fresh context window", + parameters: z.object({ prompt: z.string() }), + isTerminal: true, + defer: "never", + handler: async ({ prompt }) => { + const { messagesCleared } = + await session.rpc.history.clearContext({ prompt }); + return `Cleared ${messagesCleared} messages.`; + }, + }), + ], +}); +``` + +The required `prompt` becomes the first user message in the fresh context. A successful clear emits `session.context_cleared` with the number of removed messages and the initial message. + +## Terminal-tool behavior + +`isTerminal` ends the current agent turn only when the tool succeeds. A failure, denial, rejection, timeout, or input-validation error remains visible to the model so it can recover or retry. + +The option follows each language's naming conventions: + +| SDK | Tool option | +|---|---| +| Node.js | `isTerminal` | +| Python | `is_terminal` | +| Go | `IsTerminal` | +| .NET | `CopilotToolOptions.IsTerminal` | +| Java | `ToolDefinition.isTerminal(true)` or `@CopilotTool(isTerminal = true)` | +| Rust | `with_is_terminal(true)` | + +Use terminality only for tools whose successful completion should end the turn. Ordinary tools should leave it unset. diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index 795c0f5fd..3238c59d9 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -89,6 +89,7 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b | Agent management | `session.rpc.agent.*` | List, select, deselect, get current agent | | Fleet mode | `session.rpc.fleet.start()` | Parallel sub-agent execution; see [Fleet mode](../features/fleet-mode.md) | | Manual compaction | `session.rpc.history.compact()` | Trigger compaction on demand | +| Context clearing | `session.rpc.history.clearContext()` | Replace conversation context from a terminal tool | | History truncation | `session.rpc.history.truncate()` | Remove events from a point onward | | Session forking | `server.rpc.sessions.fork()` | Fork a session at a point in history | diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 03691fc63..228b11560 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -2793,7 +2793,8 @@ internal record ToolDefinition( bool? OverridesBuiltInTool = null, bool? SkipPermission = null, CopilotToolDefer? Defer = null, - IDictionary? Metadata = null) + IDictionary? Metadata = null, + bool? IsTerminal = null) { public static ToolDefinition FromAIFunction(AIFunctionDeclaration function) { @@ -2801,11 +2802,13 @@ public static ToolDefinition FromAIFunction(AIFunctionDeclaration function) var skipPerm = function.AdditionalProperties.TryGetValue(CopilotTool.SkipPermissionKey, out var skipVal) && skipVal is true; var defer = function.AdditionalProperties.TryGetValue(CopilotTool.DeferKey, out var deferVal) && deferVal is CopilotToolDefer d ? d : (CopilotToolDefer?)null; var metadata = function.AdditionalProperties.TryGetValue(CopilotTool.MetadataKey, out var metaVal) && metaVal is IDictionary m ? m : null; + var isTerminal = function.AdditionalProperties.TryGetValue(CopilotTool.IsTerminalKey, out var terminalVal) && terminalVal is true; return new ToolDefinition(function.Name, function.Description, function.JsonSchema, overrides ? true : null, skipPerm ? true : null, defer, - metadata); + metadata, + isTerminal ? true : null); } } diff --git a/dotnet/src/CopilotTool.cs b/dotnet/src/CopilotTool.cs index e22296bcc..ca62ccc5d 100644 --- a/dotnet/src/CopilotTool.cs +++ b/dotnet/src/CopilotTool.cs @@ -18,6 +18,9 @@ public static class CopilotTool /// The key used in to indicate that a tool can execute without a permission prompt. internal const string SkipPermissionKey = "skip_permission"; + /// The key used in to indicate that a successful call to the tool ends the agent turn. + internal const string IsTerminalKey = "is_terminal"; + /// The key used in to carry the tool's deferral mode. internal const string DeferKey = "defer"; @@ -91,7 +94,7 @@ static void ApplyToolInvocationBinding(AIFunctionFactoryOptions factoryOptions) static void ApplyToolOptions(AIFunctionFactoryOptions factoryOptions, CopilotToolOptions? toolOptions) { - if (toolOptions is not null && (toolOptions.OverridesBuiltInTool || toolOptions.SkipPermission || toolOptions.Defer is not null || toolOptions.Metadata is not null)) + if (toolOptions is not null && (toolOptions.OverridesBuiltInTool || toolOptions.SkipPermission || toolOptions.IsTerminal || toolOptions.Defer is not null || toolOptions.Metadata is not null)) { Dictionary additionalProperties = new(StringComparer.Ordinal); if (factoryOptions.AdditionalProperties is not null) @@ -112,6 +115,11 @@ static void ApplyToolOptions(AIFunctionFactoryOptions factoryOptions, CopilotToo additionalProperties[SkipPermissionKey] = true; } + if (toolOptions.IsTerminal) + { + additionalProperties[IsTerminalKey] = true; + } + if (toolOptions.Defer is { } defer) { additionalProperties[DeferKey] = defer; @@ -152,6 +160,16 @@ public sealed class CopilotToolOptions /// public bool SkipPermission { get; set; } + /// + /// Gets or sets a value indicating whether a successful call to this tool ends the agent turn. + /// + /// + /// When true, the runtime's tool phase halts after a successful call instead of feeding the result back to the + /// model for another round. A failed call leaves the loop running so the model can read the error and retry. + /// The resulting includes "is_terminal": true in its . + /// + public bool IsTerminal { get; set; } + /// /// Gets or sets a value controlling whether this tool may be deferred (loaded lazily via tool search) rather than always pre-loaded. /// diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index a30e6d303..7b7d8c109 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -287,6 +287,39 @@ public async Task SessionRequests_Serialize_AdditionalDirectories() value => Assert.Equal("/repo/resumed", value.GetString())); } + [Fact] + public async Task SessionRequests_Serialize_Terminal_Tools() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var terminalTool = CopilotTool.DefineTool( + (Func)(() => "done"), + new CopilotToolOptions { IsTerminal = true }); + var plainTool = CopilotTool.DefineTool((Func)(() => "continue")); + + await using var created = await client.CreateSessionAsync(new SessionConfig + { + Tools = [terminalTool, plainTool], + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var createRequest = Assert.Single(server.Requests, request => request.Method == "session.create"); + var createTools = createRequest.Params.GetProperty("tools"); + Assert.True(createTools[0].GetProperty("isTerminal").GetBoolean()); + Assert.False(createTools[1].TryGetProperty("isTerminal", out _)); + + server.ClearRequests(); + + await using var resumed = await client.ResumeSessionAsync("resume-with-terminal-tool", new ResumeSessionConfig + { + Tools = [terminalTool], + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var resumeRequest = Assert.Single(server.Requests, request => request.Method == "session.resume"); + Assert.True(resumeRequest.Params.GetProperty("tools")[0].GetProperty("isTerminal").GetBoolean()); + } + [Fact] public async Task CreateSessionAsync_Registers_McpAuth_Interest_Only_When_Handler_Configured() { diff --git a/dotnet/test/Unit/CopilotToolTests.cs b/dotnet/test/Unit/CopilotToolTests.cs index c3f586149..19fa6258b 100644 --- a/dotnet/test/Unit/CopilotToolTests.cs +++ b/dotnet/test/Unit/CopilotToolTests.cs @@ -34,6 +34,28 @@ public void DefineTool_Sets_Name_Description_And_Copilot_Metadata() Assert.Equal(CopilotToolDefer.Auto, defer); } + [Fact] + public void DefineTool_Sets_IsTerminal_Metadata() + { + var function = CopilotTool.DefineTool( + ReturnsOk, + new CopilotToolOptions + { + IsTerminal = true + }); + + Assert.True(function.AdditionalProperties.TryGetValue("is_terminal", out var isTerminal)); + Assert.True((bool)isTerminal!); + } + + [Fact] + public void DefineTool_Omits_IsTerminal_When_Not_Set() + { + var function = CopilotTool.DefineTool(ReturnsOk); + + Assert.False(function.AdditionalProperties.ContainsKey("is_terminal")); + } + [Fact] public void DefineTool_Omits_Copilot_Metadata_When_Flags_Are_False() { diff --git a/go/client_test.go b/go/client_test.go index 3cc123898..b5274cfda 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3526,3 +3526,40 @@ func TestResumeSessionRequest_ExpAssignments(t *testing.T) { } }) } + +func TestIsTerminal(t *testing.T) { + t.Run("IsTerminal is serialized in tool definition", func(t *testing.T) { + tool := Tool{ + Name: "clear_context", + Description: "Clear the conversation", + IsTerminal: true, + Handler: func(_ ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }, + } + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["isTerminal"] != true { + t.Errorf("Expected isTerminal to be true, got %v", m["isTerminal"]) + } + }) + + t.Run("IsTerminal is omitted when false", func(t *testing.T) { + tool := Tool{Name: "plain", Description: "A plain tool"} + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if _, ok := m["isTerminal"]; ok { + t.Error("Expected isTerminal to be omitted when false") + } + }) +} diff --git a/go/types.go b/go/types.go index e44d91acd..6669564bf 100644 --- a/go/types.go +++ b/go/types.go @@ -1517,6 +1517,11 @@ type Tool struct { Parameters map[string]any `json:"parameters,omitzero"` OverridesBuiltInTool bool `json:"overridesBuiltInTool,omitempty"` SkipPermission bool `json:"skipPermission,omitempty"` + // IsTerminal reports that a successful call to this tool ends the agent + // turn: the runtime halts instead of feeding the result back to the model + // for another round. A failed call leaves the loop running so the model can + // read the error and retry. + IsTerminal bool `json:"isTerminal,omitempty"` // Defer controls whether the tool may be deferred (loaded lazily via tool // search) rather than always pre-loaded. When empty, the runtime decides. Defer ToolDefer `json:"defer,omitempty"` diff --git a/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java b/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java index ccf0ef530..de274b66a 100644 --- a/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java +++ b/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java @@ -78,6 +78,11 @@ * @param metadata * opaque, host-defined metadata; keys are namespaced and not part of * the stable public API; {@code null} when unset + * @param isTerminal + * when {@code true}, a successful call to this tool ends the agent + * turn: the runtime's tool phase halts instead of feeding the result + * back to the model for another round; {@code null} or {@code false} + * leaves the turn running * @see SessionConfig#setTools(java.util.List) * @see ToolHandler * @since 1.0.0 @@ -87,13 +92,13 @@ public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("d @JsonProperty("parameters") Object parameters, @JsonIgnore ToolHandler handler, @JsonProperty("overridesBuiltInTool") Boolean overridesBuiltInTool, @JsonProperty("skipPermission") Boolean skipPermission, @JsonProperty("defer") ToolDefer defer, - @JsonProperty("metadata") Map metadata) { + @JsonProperty("metadata") Map metadata, @JsonProperty("isTerminal") Boolean isTerminal) { /** - * Creates a tool definition without a {@code metadata} bag. + * Creates a tool definition without a {@code metadata} bag or terminality hint. *

* Convenience overload equivalent to the canonical constructor with - * {@code metadata} set to {@code null}. + * {@code metadata} and {@code isTerminal} set to {@code null}. * * @param name * the unique name of the tool @@ -114,7 +119,37 @@ public record ToolDefinition(@JsonProperty("name") String name, @JsonProperty("d */ public ToolDefinition(String name, String description, Object parameters, ToolHandler handler, Boolean overridesBuiltInTool, Boolean skipPermission, ToolDefer defer) { - this(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, null); + this(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, null, null); + } + + /** + * Creates a tool definition without a terminality hint. + *

+ * Convenience overload equivalent to the canonical constructor with + * {@code isTerminal} set to {@code null}. + * + * @param name + * the unique name of the tool + * @param description + * a description of what the tool does + * @param parameters + * the JSON Schema for the tool's parameters + * @param handler + * the handler function to execute when invoked + * @param overridesBuiltInTool + * whether this tool overrides a built-in tool; {@code null} for the + * default + * @param skipPermission + * whether the tool may run without a permission check; {@code null} + * for the default + * @param defer + * the deferral mode; {@code null} lets the runtime decide + * @param metadata + * the opaque, host-defined metadata; {@code null} when unset + */ + public ToolDefinition(String name, String description, Object parameters, ToolHandler handler, + Boolean overridesBuiltInTool, Boolean skipPermission, ToolDefer defer, Map metadata) { + this(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, metadata, null); } /** @@ -304,7 +339,8 @@ public static List fromClass(Class clazz) { */ @CopilotExperimental public ToolDefinition overridesBuiltInTool(boolean value) { - return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer, metadata); + return new ToolDefinition(name, description, parameters, handler, value, skipPermission, defer, metadata, + isTerminal); } /** @@ -318,7 +354,8 @@ public ToolDefinition overridesBuiltInTool(boolean value) { */ @CopilotExperimental public ToolDefinition skipPermission(boolean value) { - return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer, metadata); + return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, value, defer, metadata, + isTerminal); } /** @@ -333,7 +370,7 @@ public ToolDefinition skipPermission(boolean value) { @CopilotExperimental public ToolDefinition defer(ToolDefer value) { return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, value, - metadata); + metadata, isTerminal); } /** @@ -348,7 +385,22 @@ public ToolDefinition defer(ToolDefer value) { @CopilotExperimental public ToolDefinition metadata(Map value) { return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, - value); + value, isTerminal); + } + + /** + * Returns a copy with the {@code isTerminal} flag set. + * + * @param value + * {@code true} to end the agent turn after a successful call to this + * tool + * @return a new {@code ToolDefinition} with the flag applied + * @since 1.0.11 + */ + @CopilotExperimental + public ToolDefinition isTerminal(boolean value) { + return new ToolDefinition(name, description, parameters, handler, overridesBuiltInTool, skipPermission, defer, + metadata, value); } // ------------------------------------------------------------------ diff --git a/java/src/main/java/com/github/copilot/tool/CopilotTool.java b/java/src/main/java/com/github/copilot/tool/CopilotTool.java index db9e3ca62..28cd75928 100644 --- a/java/src/main/java/com/github/copilot/tool/CopilotTool.java +++ b/java/src/main/java/com/github/copilot/tool/CopilotTool.java @@ -48,6 +48,9 @@ /** Whether to skip permission checks. */ boolean skipPermission() default false; + /** Whether a successful call to this tool ends the agent turn. */ + boolean isTerminal() default false; + /** Defer configuration for this tool. */ ToolDefer defer() default ToolDefer.NONE; diff --git a/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java b/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java index dbc6921af..f88c1ac7c 100644 --- a/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java +++ b/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java @@ -317,6 +317,7 @@ private void writeToolDefinition(PrintWriter out, ExecutableElement method) { String description = annotation.value(); boolean overridesBuiltIn = annotation.overridesBuiltInTool(); boolean skipPermission = annotation.skipPermission(); + boolean isTerminal = annotation.isTerminal(); com.github.copilot.rpc.ToolDefer defer = annotation.defer(); // Generate schema with @CopilotToolParam metadata (descriptions, names, @@ -329,6 +330,7 @@ private void writeToolDefinition(PrintWriter out, ExecutableElement method) { // Use the record constructor directly so all flags apply independently String overridesArg = overridesBuiltIn ? "Boolean.TRUE" : "null"; String skipPermArg = skipPermission ? "Boolean.TRUE" : "null"; + String isTerminalArg = isTerminal ? "Boolean.TRUE" : "null"; String deferArg = defer != com.github.copilot.rpc.ToolDefer.NONE ? "ToolDefer." + defer.name() : "null"; out.println(" new ToolDefinition("); @@ -341,7 +343,8 @@ private void writeToolDefinition(PrintWriter out, ExecutableElement method) { out.println(" " + overridesArg + ","); out.println(" " + skipPermArg + ","); out.println(" " + deferArg + ","); - out.println(" " + metadataSource(annotation)); + out.println(" " + metadataSource(annotation) + ","); + out.println(" " + isTerminalArg); out.print(" )"); } diff --git a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java b/java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java new file mode 100644 index 000000000..850dfa251 --- /dev/null +++ b/java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot.rpc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import org.junit.jupiter.api.Test; + +/** Wire-level coverage for {@link ToolDefinition#isTerminal()}. */ +class ToolDefinitionIsTerminalTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + void isTerminalSerializesAsCamelCaseWhenSet() throws Exception { + ToolDefinition definition = new ToolDefinition("clear_context", "Clear the conversation", + Map.of("type", "object"), null, null, null, null, null, true); + + JsonNode node = MAPPER.valueToTree(definition); + + assertTrue(node.has("isTerminal"), "isTerminal should be serialized"); + assertTrue(node.get("isTerminal").asBoolean(), "isTerminal should be true"); + } + + @Test + void isTerminalIsOmittedWhenNull() throws Exception { + ToolDefinition definition = new ToolDefinition("plain", "A plain tool", Map.of("type", "object"), null, null, + null, null, null, null); + + JsonNode node = MAPPER.valueToTree(definition); + + assertFalse(node.has("isTerminal"), "isTerminal should be omitted when null"); + } + + @Test + void sevenArgumentConstructorStillCompilesAndLeavesTerminalityUnset() throws Exception { + // Guards source compatibility for call sites written before isTerminal + // was added as a record component. + ToolDefinition definition = new ToolDefinition("legacy", "Legacy call site", Map.of("type", "object"), null, + null, null, null); + + assertEquals(null, definition.isTerminal()); + assertFalse(MAPPER.valueToTree(definition).has("isTerminal")); + } +} diff --git a/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java b/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java index f0623d92e..e7012c644 100644 --- a/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java +++ b/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java @@ -620,8 +620,8 @@ public String doSomething(@CopilotToolParam("Input") String input) { assertFalse(generated.contains("Map.of("), "Expected no metadata map for a tool without metadata, got:\n" + generated); String normalizedGenerated = generated.replace("\r\n", "\n").replace('\r', '\n'); - assertTrue(normalizedGenerated.contains(" null\n )"), - "Expected metadata constructor argument to be null when metadata is absent, got:\n" + generated); + assertTrue(normalizedGenerated.contains(" null,\n null\n )"), + "Expected metadata and isTerminal constructor arguments to be null when absent, got:\n" + generated); } @Test @@ -1243,6 +1243,28 @@ public String grep(@CopilotToolParam("Query") String query) { "Expected Boolean.TRUE for overridesBuiltInTool, got:\n" + generated); } + @Test + void generatesTerminalTool_whenIsTerminal() { + String source = """ + package test; + import com.github.copilot.tool.CopilotTool; + public class TerminalTools { + @CopilotTool(value = "Ends the turn", isTerminal = true) + public String finish() { + return "done"; + } + } + """; + + CompilationResult result = compileWithProcessor(List.of(inMemorySource("test.TerminalTools", source))); + assertNoErrors(result); + String generated = result.getGeneratedSource("test.TerminalTools$$CopilotToolMeta"); + String normalizedGenerated = generated.replace("\r\n", "\n").replace('\r', '\n'); + assertTrue(normalizedGenerated.contains( + " null,\n null,\n null,\n null,\n Boolean.TRUE\n )"), + "Expected Boolean.TRUE for isTerminal in the final constructor position, got:\n" + generated); + } + // ── Test: Combined flags all apply independently ──────────────────────────── @Test @@ -1252,7 +1274,8 @@ void generatesCombinedFlags() { import com.github.copilot.tool.CopilotTool; import com.github.copilot.rpc.ToolDefer; public class CombinedTools { - @CopilotTool(value = "Combined", overridesBuiltInTool = true, skipPermission = true, defer = ToolDefer.AUTO) + @CopilotTool(value = "Combined", overridesBuiltInTool = true, skipPermission = true, + isTerminal = true, defer = ToolDefer.AUTO) public String doAll() { return "done"; } @@ -1268,11 +1291,10 @@ public String doAll() { assertTrue(generated.contains("Boolean.TRUE"), "Expected Boolean.TRUE for override/skipPermission, got:\n" + generated); assertTrue(generated.contains("ToolDefer.AUTO"), "Expected ToolDefer.AUTO, got:\n" + generated); - // Count Boolean.TRUE occurrences — should be 2 (overridesBuiltInTool + - // skipPermission) + // Count Boolean.TRUE occurrences — override, skipPermission, and isTerminal. long boolCount = generated.lines().filter(l -> l.contains("Boolean.TRUE")).count(); - assertEquals(2, boolCount, - "Expected 2 Boolean.TRUE lines (overridesBuiltInTool + skipPermission), got:\n" + generated); + assertEquals(3, boolCount, + "Expected 3 Boolean.TRUE lines (override + skipPermission + isTerminal), got:\n" + generated); } // ── Test: ToolDefer.NONE results in regular create ────────────────────────── diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index b4fe0f463..4ed139be7 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1535,6 +1535,7 @@ export class CopilotClient { skipPermission: tool.skipPermission, defer: tool.defer, metadata: tool.metadata, + isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, canvases: config.canvases?.map((canvas) => canvas.declaration), @@ -1785,6 +1786,7 @@ export class CopilotClient { skipPermission: tool.skipPermission, defer: tool.defer, metadata: tool.metadata, + isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, canvases: config.canvases?.map((canvas) => canvas.declaration), diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 035ab6563..d77a3a97c 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -656,6 +656,17 @@ export interface Tool { * Unknown keys are preserved and round-tripped untouched. */ metadata?: Record; + /** + * When true, a successful call to this tool ends the agent turn: the runtime's + * tool phase halts instead of feeding the tool result back to the model for + * another round. A failed call (for example input validation) leaves the loop + * running so the model can read the error and retry. + * + * Use this for tools whose whole purpose is to terminate the turn, such as a + * context clear that replaces the conversation the model would otherwise + * continue from. + */ + isTerminal?: boolean; } /** @@ -672,6 +683,7 @@ export function defineTool( skipPermission?: boolean; defer?: "auto" | "never"; metadata?: Record; + isTerminal?: boolean; } ): Tool { return { name, ...config }; diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 254a21fa4..962d90970 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -714,6 +714,68 @@ describe("CopilotClient", () => { expect(createPayload.tools[0].metadata).toBeUndefined(); }); + it("forwards tool isTerminal in session.create and session.resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + const tool = { + name: "clear_context", + description: "Clears the conversation", + parameters: { type: "object", properties: {} }, + isTerminal: true, + }; + + const session = await client.createSession({ + onPermissionRequest: approveAll, + tools: [tool], + }); + await client.resumeSession(session.sessionId, { + onPermissionRequest: approveAll, + tools: [tool], + }); + + const createPayload = spy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + const resumePayload = spy.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as any; + expect(createPayload.tools[0].isTerminal).toBe(true); + expect(resumePayload.tools[0].isTerminal).toBe(true); + }); + + it("omits tool isTerminal from session.create when unset", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => client.forceStop()); + + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + + await client.createSession({ + onPermissionRequest: approveAll, + tools: [{ name: "my_tool", description: "a tool" }], + }); + + const createPayload = spy.mock.calls.find( + ([method]) => method === "session.create" + )![1] as any; + expect(createPayload.tools[0].isTerminal).toBeUndefined(); + }); + it("forwards new session options in session.create and session.resume", async () => { const client = new CopilotClient(); await client.start(); diff --git a/nodejs/test/e2e/tool_results.e2e.test.ts b/nodejs/test/e2e/tool_results.e2e.test.ts index 6e8729c42..eb6ecf6f7 100644 --- a/nodejs/test/e2e/tool_results.e2e.test.ts +++ b/nodejs/test/e2e/tool_results.e2e.test.ts @@ -59,6 +59,7 @@ describe("Tool Results", async () => { tools: [ defineTool("check_status", { description: "Checks the status of a service", + isTerminal: true, handler: (): ToolResultObject => ({ textResultForLlm: "Service unavailable", resultType: "failure", @@ -74,6 +75,7 @@ describe("Tool Results", async () => { const failureContent = assistantMessage?.data.content ?? ""; expect(failureContent).toMatch(/service is down/i); + expect(await openAiEndpoint.getExchanges()).toHaveLength(2); await session.disconnect(); }); diff --git a/nodejs/test/e2e/tools.e2e.test.ts b/nodejs/test/e2e/tools.e2e.test.ts index c505f8aa8..7ca943aa7 100644 --- a/nodejs/test/e2e/tools.e2e.test.ts +++ b/nodejs/test/e2e/tools.e2e.test.ts @@ -7,7 +7,7 @@ import { join } from "path"; import { assert, describe, expect, it } from "vitest"; import { z } from "zod"; import { defineTool, approveAll, ToolSet } from "../../src/index.js"; -import type { PermissionRequest } from "../../src/index.js"; +import type { CopilotSession, PermissionRequest, SessionEvent } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext"; describe("Custom tools", async () => { @@ -45,6 +45,46 @@ describe("Custom tools", async () => { expect(assistantMessage?.data.content).toContain("HELLO"); }); + it("clears context from a terminal tool and starts the seeded turn", async () => { + const seedPrompt = "Reply with exactly FRESH_CONTEXT."; + const events: SessionEvent[] = []; + let session: CopilotSession; + session = await client.createSession({ + onPermissionRequest: approveAll, + onEvent: (event) => events.push(event), + tools: [ + defineTool("clear_context", { + description: "Clears the conversation and starts a fresh context window", + parameters: z.object({ prompt: z.string() }), + isTerminal: true, + defer: "never", + handler: async () => { + const result = await session.rpc.history.clearContext({ + prompt: seedPrompt, + }); + return `Cleared ${result.messagesCleared} messages.`; + }, + }), + ], + }); + + const assistantMessage = await session.sendAndWait({ + prompt: `Call clear_context with prompt "${seedPrompt}" now.`, + }); + + expect(assistantMessage?.data.content).toContain("FRESH_CONTEXT"); + const contextCleared = events.find((event) => event.type === "session.context_cleared"); + expect(contextCleared).toBeDefined(); + if (contextCleared?.type === "session.context_cleared") { + expect(contextCleared.data.messagesCleared).toBeGreaterThan(0); + expect(contextCleared.data.initialMessage).toBe(seedPrompt); + } + + const traffic = await openAiEndpoint.getExchanges(); + expect(traffic).toHaveLength(2); + expect(JSON.stringify(traffic[1]?.request.messages)).toContain(seedPrompt); + }); + it("low_level_tool_definition", async () => { let currentPhase = ""; const session = await client.createSession({ diff --git a/python/copilot/client.py b/python/copilot/client.py index 61f08e641..7c273bd01 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2282,6 +2282,8 @@ async def create_session( definition["defer"] = tool.defer if tool.metadata is not None: definition["metadata"] = tool.metadata + if tool.is_terminal: + definition["isTerminal"] = True tool_defs.append(definition) # Empty-mode validation and normalization @@ -2992,6 +2994,8 @@ async def resume_session( definition["defer"] = tool.defer if tool.metadata is not None: definition["metadata"] = tool.metadata + if tool.is_terminal: + definition["isTerminal"] = True tool_defs.append(definition) # Empty-mode validation and normalization diff --git a/python/copilot/tools.py b/python/copilot/tools.py index de81fe7fd..dc709cf7d 100644 --- a/python/copilot/tools.py +++ b/python/copilot/tools.py @@ -82,6 +82,11 @@ class Tool: skip_permission: bool = False defer: Literal["auto", "never"] | None = None metadata: dict[str, Any] | None = None + #: When true, a successful call to this tool ends the agent turn: the + #: runtime halts instead of feeding the result back to the model for + #: another round. A failed call leaves the loop running so the model can + #: read the error and retry. + is_terminal: bool = False T = TypeVar("T", bound=BaseModel) @@ -97,6 +102,7 @@ def define_tool( skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, metadata: dict[str, Any] | None = None, + is_terminal: bool = False, ) -> Callable[[Callable[..., Any]], Tool]: pass @@ -112,6 +118,7 @@ def define_tool( skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, metadata: dict[str, Any] | None = None, + is_terminal: bool = False, ) -> Tool: pass @@ -127,6 +134,7 @@ def define_tool( skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, metadata: dict[str, Any] | None = None, + is_terminal: bool = False, ) -> Tool: pass @@ -141,6 +149,7 @@ def define_tool( skip_permission: bool = False, defer: Literal["auto", "never"] | None = None, metadata: dict[str, Any] | None = None, + is_terminal: bool = False, ) -> Tool | Callable[[Callable[[Any, ToolInvocation], Any]], Tool]: """ Define a tool with automatic JSON schema generation from Pydantic models. @@ -193,6 +202,10 @@ def lookup_issue(params: LookupIssueParams) -> str: Keys are namespaced and not part of the stable public API; values are not interpreted and may be recognized to inform host-specific behavior. Unknown keys are preserved. + is_terminal: When True, a successful call to this tool ends the agent turn: + the runtime halts instead of feeding the result back to the model + for another round. A failed call leaves the loop running so the + model can read the error and retry. Returns: A Tool instance @@ -288,6 +301,7 @@ async def wrapped_handler(invocation: ToolInvocation) -> ToolResult: skip_permission=skip_permission, defer=defer, metadata=metadata, + is_terminal=is_terminal, ) # If handler is provided, call decorator immediately @@ -308,6 +322,7 @@ async def wrapped_handler(invocation: ToolInvocation) -> ToolResult: skip_permission=skip_permission, defer=defer, metadata=metadata, + is_terminal=is_terminal, ) # Otherwise return decorator for @define_tool(...) usage diff --git a/python/test_client.py b/python/test_client.py index 9e116f449..f101fc396 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -793,6 +793,45 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_tool_is_terminal(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + tool = Tool(name="my_tool", description="a tool", is_terminal=True) + plain_tool = Tool(name="plain_tool", description="a tool") + + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + tools=[tool, plain_tool], + ) + await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + tools=[tool], + ) + + create_tools = captured["session.create"]["tools"] + assert create_tools[0]["isTerminal"] is True + # Omitted when left at its default. + assert "isTerminal" not in create_tools[1] + assert captured["session.resume"]["tools"][0]["isTerminal"] is True + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_forward_canvas_provider(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) diff --git a/rust/src/types.rs b/rust/src/types.rs index 895529760..37d3b248b 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -346,6 +346,12 @@ pub struct Tool { /// access control. #[serde(default, skip_serializing_if = "is_false")] pub skip_permission: bool, + /// When `true`, a successful call to this tool ends the agent turn: the + /// runtime's tool phase halts instead of feeding the result back to the + /// model for another round. A failed call leaves the loop running so the + /// model can read the error and retry. + #[serde(default, skip_serializing_if = "is_false")] + pub is_terminal: bool, /// Controls whether the tool may be deferred (loaded lazily via tool /// search) rather than always pre-loaded. When [`DeferMode::Auto`], the /// tool can be deferred and surfaced through tool search. When @@ -470,6 +476,18 @@ impl Tool { self } + /// Sets whether a successful call to this tool ends the agent turn. + /// + /// When `true`, the runtime's tool phase halts after a successful call + /// instead of feeding the result back to the model for another round. A + /// failed call leaves the loop running so the model can read the error and + /// retry. + #[must_use] + pub fn with_is_terminal(mut self, is_terminal: bool) -> Self { + self.is_terminal = is_terminal; + self + } + /// Set the deferral mode controlling whether the tool may be loaded /// lazily via tool search ([`DeferMode::Auto`]) or always pre-loaded /// ([`DeferMode::Never`]). @@ -512,6 +530,7 @@ impl std::fmt::Debug for Tool { .field("parameters", &self.parameters) .field("overrides_built_in_tool", &self.overrides_built_in_tool) .field("skip_permission", &self.skip_permission) + .field("is_terminal", &self.is_terminal) .field("defer", &self.defer) .field("metadata", &self.metadata) .field( @@ -7602,3 +7621,50 @@ mod permission_builder_tests { assert!(json.get("isExperimentalMode").is_none()); } } + +#[cfg(test)] +mod is_terminal_tests { + use super::Tool; + + #[test] + fn is_terminal_serializes_as_camel_case_when_set() { + let tool = Tool { + name: "clear_context".to_owned(), + is_terminal: true, + ..Default::default() + }; + let value = serde_json::to_value(&tool).expect("tool serializes"); + assert_eq!( + value.get("isTerminal"), + Some(&serde_json::Value::Bool(true)) + ); + } + + #[test] + fn is_terminal_is_omitted_when_false() { + let tool = Tool { + name: "plain".to_owned(), + ..Default::default() + }; + let value = serde_json::to_value(&tool).expect("tool serializes"); + assert!(value.get("isTerminal").is_none()); + } + + /// `Tool` has a hand-written `Debug` impl, so a new field is only reported + /// if it is added there by hand. Guard against that drift. + #[test] + fn is_terminal_appears_in_debug_output() { + let terminal = Tool { + name: "clear_context".to_owned(), + is_terminal: true, + ..Default::default() + }; + assert!(format!("{terminal:?}").contains("is_terminal: true")); + + let plain = Tool { + name: "plain".to_owned(), + ..Default::default() + }; + assert!(format!("{plain:?}").contains("is_terminal: false")); + } +} diff --git a/test/snapshots/tools/clears_context_from_a_terminal_tool_and_starts_the_seeded_turn.yaml b/test/snapshots/tools/clears_context_from_a_terminal_tool_and_starts_the_seeded_turn.yaml new file mode 100644 index 000000000..36d5adce4 --- /dev/null +++ b/test/snapshots/tools/clears_context_from_a_terminal_tool_and_starts_the_seeded_turn.yaml @@ -0,0 +1,22 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Call clear_context with prompt "Reply with exactly FRESH_CONTEXT." now. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: clear_context + arguments: '{"prompt":"Reply with exactly FRESH_CONTEXT."}' + - messages: + - role: system + content: ${system} + - role: user + content: Reply with exactly FRESH_CONTEXT. + - role: assistant + content: FRESH_CONTEXT From dece5d3a79dcdaafb41bf25035afa23c0320d845 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 21:20:46 -0400 Subject: [PATCH 42/72] Update @github/copilot to 1.0.79-5 (#2282) * Update @github/copilot to 1.0.79-5 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix generated schema call sites Supply the new optional permission decision context in Java and Rust while preserving legacy behavior. Update Java generated-record coverage for the new skill command name field. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Generated/Rpc.cs | 425 +++++++++++++- dotnet/src/Generated/SessionEvents.cs | 34 +- go/rpc/zrpc.go | 336 ++++++++++- go/rpc/zrpc_encoding.go | 10 +- go/rpc/zsession_events.go | 24 +- go/zsession_events.go | 2 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +-- java/scripts/codegen/package.json | 2 +- .../ManagedSettingsResolvedSource.java | 6 +- .../SessionManagedSettingsResolvedEvent.java | 10 +- .../copilot/generated/SkillsLoadedSkill.java | 2 + .../rpc/DisableBypassPermissionsMode.java | 28 + .../generated/rpc/ExtensionLaunchProfile.java | 33 ++ ...ExtensionLaunchProviderResolveRequest.java | 33 ++ .../ExtensionLaunchProviderResolveResult.java | 30 + .../rpc/ManagedSettingsReadResult.java | 32 ++ .../rpc/PermissionDecisionContext.java | 31 + .../rpc/PermissionDecisionOutcome.java | 37 ++ .../rpc/PermissionDecisionSource.java | 39 ++ .../rpc/PermissionDecisionSurface.java | 39 ++ .../copilot/generated/rpc/SandboxConfig.java | 4 +- .../rpc/ServerManagedSettingsApi.java | 40 ++ .../copilot/generated/rpc/ServerRpc.java | 14 + .../copilot/generated/rpc/ServerSkill.java | 2 + .../rpc/SessionManagedPermissions.java | 34 ++ .../generated/rpc/SessionManagedSettings.java | 26 + ...onMetadataGetContextAttributionResult.java | 35 ++ .../generated/rpc/SessionOpenOptions.java | 4 + ...sHandlePendingPermissionRequestParams.java | 4 +- .../github/copilot/generated/rpc/Skill.java | 2 + .../com/github/copilot/CopilotSession.java | 11 +- .../rpc/GeneratedRpcApiCoverageTest.java | 2 +- .../rpc/GeneratedRpcRecordsCoverageTest.java | 8 +- nodejs/package-lock.json | 72 +-- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 278 ++++++++- nodejs/src/generated/session-events.ts | 28 +- python/copilot/generated/rpc.py | 534 ++++++++++++++++-- python/copilot/generated/session_events.py | 24 +- rust/examples/manual_tool_resume.rs | 1 + rust/src/generated/api_types.rs | 357 +++++++++++- rust/src/generated/rpc.rs | 62 ++ rust/src/generated/session_events.rs | 28 +- rust/tests/e2e/rpc_tasks_and_handlers.rs | 1 + test/harness/package-lock.json | 72 +-- test/harness/package.json | 2 +- 48 files changed, 2638 insertions(+), 238 deletions(-) create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 32778850b..604ed0960 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -1564,6 +1564,10 @@ public sealed class ServerSkill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + ///

Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; @@ -2036,6 +2040,19 @@ internal sealed class UserSettingsSetRequest public JsonElement Settings { get; set; } } +/// Validated device-managed settings discovered before a session exists. +[Experimental(Diagnostics.Experimental)] +public sealed class ManagedSettingsReadResult +{ + /// Discovery or validation error text when managed settings could not be read safely. + [JsonPropertyName("errorMessage")] + public string? ErrorMessage { get; set; } + + /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + [JsonPropertyName("settingsJson")] + public JsonElement? SettingsJson { get; set; } +} + /// Indicates whether the calling client was registered as the session filesystem provider. [Experimental(Diagnostics.Experimental)] public sealed class SessionFsSetProviderResult @@ -6734,6 +6751,10 @@ public sealed class Skill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; @@ -8739,9 +8760,9 @@ public sealed class SandboxConfig [JsonPropertyName("addCurrentWorkingDirectory")] public bool? AddCurrentWorkingDirectory { get; set; } - /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). - [JsonPropertyName("allowDevToolCaches")] - public bool? AllowDevToolCaches { get; set; } + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). + [JsonPropertyName("allowDevToolAccess")] + public bool? AllowDevToolAccess { get; set; } /// Whether sandboxing is enabled for the session. [JsonPropertyName("enabled")] @@ -10608,6 +10629,23 @@ public sealed class PermissionRequestResult public bool Success { get; set; } } +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +[Experimental(Diagnostics.Experimental)] +public sealed class PermissionDecisionContext +{ + /// Disposition of the permission request as observed by the responding client. + [JsonPropertyName("outcome")] + public PermissionDecisionOutcome Outcome { get; set; } + + /// Controlled reason or actor responsible for the response. + [JsonPropertyName("source")] + public PermissionDecisionSource Source { get; set; } + + /// Client surface that submitted the response. + [JsonPropertyName("surface")] + public PermissionDecisionSurface Surface { get; set; } +} + /// The client's response to the pending permission prompt. /// Polymorphic base type discriminated by kind. [Experimental(Diagnostics.Experimental)] @@ -11188,6 +11226,10 @@ public partial class PermissionDecisionDeniedByPermissionRequestHook : Permissio [Experimental(Diagnostics.Experimental)] internal sealed class PermissionDecisionRequest { + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + /// Request ID of the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; @@ -12122,6 +12164,38 @@ internal sealed class MetadataContextInfoRequest public string SessionId { get; set; } = string.Empty; } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +public sealed class MetadataContextAttributionResultContextAttributionCategories +{ + /// Output reserve plus post-blocking-threshold buffer. + [JsonPropertyName("buffer")] + public long Buffer { get; set; } + + /// Custom-instructions tokens (0 when none are configured). + [JsonPropertyName("customInstructions")] + public long CustomInstructions { get; set; } + + /// Remaining unused window capacity (clamped at 0). + [JsonPropertyName("freeSpace")] + public long FreeSpace { get; set; } + + /// MCP tool-definition tokens. + [JsonPropertyName("mcpTools")] + public long McpTools { get; set; } + + /// Conversation (user/assistant/tool) message tokens. + [JsonPropertyName("messages")] + public long Messages { get; set; } + + /// System prompt tokens, excluding custom instructions. + [JsonPropertyName("systemPrompt")] + public long SystemPrompt { get; set; } + + /// Non-MCP tool-definition tokens. + [JsonPropertyName("systemTools")] + public long SystemTools { get; set; } +} + /// Successful compaction history for the session. public sealed class MetadataContextAttributionResultContextAttributionCompactions { @@ -12161,14 +12235,42 @@ public sealed class MetadataContextAttributionResultContextAttributionEntry /// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. public sealed class MetadataContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + [JsonPropertyName("bufferTokens")] + public long BufferTokens { get; set; } + + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + [JsonPropertyName("categories")] + public MetadataContextAttributionResultContextAttributionCategories Categories { get => field ??= new(); set; } + /// Successful compaction history for the session. [JsonPropertyName("compactions")] public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } + /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. [JsonPropertyName("entries")] public IList Entries { get => field ??= []; set; } + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + [JsonPropertyName("limit")] + public long Limit { get; set; } + + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + [JsonPropertyName("modelSource")] + public string ModelSource { get; set; } = string.Empty; + + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } + /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. [JsonPropertyName("totalTokens")] public long TotalTokens { get; set; } @@ -14937,6 +15039,55 @@ public sealed class CanvasProviderInvokeActionRequest public string SessionId { get; set; } = string.Empty; } +/// Opaque integrator-owned process launch profile for one extension entrypoint. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProfile +{ + /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + [JsonPropertyName("args")] + public IList Args { get => field ??= []; set; } + + /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + [JsonPropertyName("env")] + public IDictionary Env { get => field ??= new Dictionary(); set; } + + /// Executable used to launch the extension entrypoint. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("executable")] + public string Executable { get; set; } = string.Empty; +} + +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProviderResolveResult +{ + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + [JsonPropertyName("launch")] + public ExtensionLaunchProfile? Launch { get; set; } +} + +/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. +[Experimental(Diagnostics.Experimental)] +public sealed class ExtensionLaunchProviderResolveRequest +{ + /// Source-qualified extension identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Absolute path to the discovered extension entrypoint. + [JsonPropertyName("modulePath")] + public string ModulePath { get; set; } = string.Empty; + + /// Human-readable extension name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Discovery source for the extension entrypoint. + [JsonPropertyName("source")] + public ExtensionSource Source { get; set; } +} + /// Acknowledgement. Returning successfully simply means the SDK accepted the start frame; it does not imply the request will succeed. [Experimental(Diagnostics.Experimental)] public sealed class LlmInferenceHttpRequestStartResult @@ -20936,6 +21087,210 @@ public override void Write(Utf8JsonWriter writer, PermissionsConfigureAdditional } +/// Disposition of a permission request as observed by the responding client. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionOutcome : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionOutcome(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The request was approved automatically without a new human decision. + public static PermissionDecisionOutcome AutoApproved { get; } = new("auto_approved"); + + /// The request was denied without an interactive user decision; source records why. + public static PermissionDecisionOutcome AutopilotDenied { get; } = new("autopilot_denied"); + + /// The response came from an interactive user prompt. + public static PermissionDecisionOutcome PromptedUser { get; } = new("prompted_user"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionOutcome left, PermissionDecisionOutcome right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionOutcome left, PermissionDecisionOutcome right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionOutcome other && Equals(other); + + /// + public bool Equals(PermissionDecisionOutcome other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionOutcome value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionOutcome)); + } + } +} + + +/// Controlled reason or actor responsible for a permission response. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionSource : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionSource(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The response followed the auto-approval judge recommendation. + public static PermissionDecisionSource JudgeRecommendation { get; } = new("judge_recommendation"); + + /// A human supplied the response through an interactive prompt. + public static PermissionDecisionSource HumanResponse { get; } = new("human_response"); + + /// The host applied a standing policy or override rather than a judge recommendation or human decision. + public static PermissionDecisionSource HostPolicy { get; } = new("host_policy"); + + /// The host denied the request because no interactive user response was available. + public static PermissionDecisionSource UnattendedFallback { get; } = new("unattended_fallback"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionSource left, PermissionDecisionSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionSource left, PermissionDecisionSource right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionSource other && Equals(other); + + /// + public bool Equals(PermissionDecisionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionSource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionSource)); + } + } +} + + +/// Client surface that submitted a permission response. +[Experimental(Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct PermissionDecisionSurface : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public PermissionDecisionSurface(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The interactive Copilot CLI terminal UI. + public static PermissionDecisionSurface Tui { get; } = new("tui"); + + /// The non-interactive Copilot CLI prompt mode. + public static PermissionDecisionSurface PromptMode { get; } = new("prompt_mode"); + + /// The Copilot App client. + public static PermissionDecisionSurface CopilotApp { get; } = new("copilot_app"); + + /// A generic Copilot SDK client. + public static PermissionDecisionSurface Sdk { get; } = new("sdk"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PermissionDecisionSurface left, PermissionDecisionSurface right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PermissionDecisionSurface left, PermissionDecisionSurface right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is PermissionDecisionSurface other && Equals(other); + + /// + public bool Equals(PermissionDecisionSurface other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override PermissionDecisionSurface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, PermissionDecisionSurface value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionDecisionSurface)); + } + } +} + + /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. [Experimental(Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -23035,6 +23390,14 @@ internal async Task ConnectAsync(string? token = null, bool? enab return await CopilotClient.InvokeRpcAsync(_rpc, "connect", [request], cancellationToken); } + /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + /// The to monitor for cancellation requests. The default is . + [Experimental(Diagnostics.Experimental)] + public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancellationToken = default) + { + await CopilotClient.InvokeRpcAsync(_rpc, "registerExtensionLaunchProvider", [], cancellationToken); + } + /// Models APIs. public ServerModelsApi Models => field ?? @@ -23107,6 +23470,12 @@ internal async Task ConnectAsync(string? token = null, bool? enab Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; + /// ManagedSettings APIs. + public ServerManagedSettingsApi ManagedSettings => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; + /// Runtime APIs. public ServerRuntimeApi Runtime => field ?? @@ -23806,6 +24175,26 @@ public async Task SetAsync(object settings, CancellationT } } +/// Provides server-scoped ManagedSettings APIs. +[Experimental(Diagnostics.Experimental)] +public sealed class ServerManagedSettingsApi +{ + private readonly JsonRpc _rpc; + + internal ServerManagedSettingsApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + /// The to monitor for cancellation requests. The default is . + /// Validated device-managed settings discovered before a session exists. + public async Task ReadAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "managedSettings.read", [], cancellationToken); + } +} + /// Provides server-scoped Runtime APIs. [Experimental(Diagnostics.Experimental)] public sealed class ServerRuntimeApi @@ -27006,15 +27395,16 @@ public async Task ConfigureAsync(bool? approveAllToo /// Provides a decision for a pending tool permission request. /// Request ID of the pending permission request. /// The client's response to the pending permission prompt. + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. /// The to monitor for cancellation requests. The default is . /// Indicates whether the permission decision was applied; false when the request was already resolved. - public async Task HandlePendingPermissionRequestAsync(string requestId, PermissionDecision result, CancellationToken cancellationToken = default) + public async Task HandlePendingPermissionRequestAsync(string requestId, PermissionDecision result, PermissionDecisionContext? decisionContext = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(requestId); ArgumentNullException.ThrowIfNull(result); _session.ThrowIfDisposed(); - var request = new PermissionDecisionRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result }; + var request = new PermissionDecisionRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result, DecisionContext = decisionContext }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.permissions.handlePendingPermissionRequest", [request], cancellationToken); } @@ -28528,6 +28918,17 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncHandles `extensionLaunchProvider` client global API methods.
+[Experimental(Diagnostics.Experimental)] +public interface IExtensionLaunchProviderHandler +{ + /// Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds. + /// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + /// The to monitor for cancellation requests. The default is . + /// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + Task ResolveAsync(ExtensionLaunchProviderResolveRequest request, CancellationToken cancellationToken = default); +} + /// Handles `llmInference` client global API methods. [Experimental(Diagnostics.Experimental)] public interface ILlmInferenceHandler @@ -28557,6 +28958,9 @@ public interface IGitHubTelemetryHandler /// Provides all client global API handler groups for a connection. public sealed class ClientGlobalApiHandlers { + /// Optional handler for ExtensionLaunchProvider client global API methods. + public IExtensionLaunchProviderHandler? ExtensionLaunchProvider { get; set; } + /// Optional handler for LlmInference client global API methods. public ILlmInferenceHandler? LlmInference { get; set; } @@ -28575,6 +28979,11 @@ internal static class ClientGlobalApiRegistration /// public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiHandlers handlers) { + rpc.SetLocalRpcMethod("extensionLaunchProvider.resolve", (Func>)(async (request, cancellationToken) => + { + var handler = handlers.ExtensionLaunchProvider ?? throw new InvalidOperationException("No extensionLaunchProvider client-global handler registered"); + return await handler.ResolveAsync(request, cancellationToken); + }), singleObjectParam: true); rpc.SetLocalRpcMethod("llmInference.httpRequestStart", (Func>)(async (request, cancellationToken) => { var handler = handlers.LlmInference ?? throw new InvalidOperationException("No llmInference client-global handler registered"); @@ -29045,6 +29454,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ExecuteCommandParams))] [JsonSerializable(typeof(ExecuteCommandResult))] [JsonSerializable(typeof(Extension))] +[JsonSerializable(typeof(ExtensionLaunchProfile))] +[JsonSerializable(typeof(ExtensionLaunchProviderResolveRequest))] +[JsonSerializable(typeof(ExtensionLaunchProviderResolveResult))] [JsonSerializable(typeof(ExtensionList))] [JsonSerializable(typeof(ExtensionsDisableRequest))] [JsonSerializable(typeof(ExtensionsEnableRequest))] @@ -29134,6 +29546,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(LogRequest))] [JsonSerializable(typeof(LogResult))] [JsonSerializable(typeof(LspInitializeRequest))] +[JsonSerializable(typeof(ManagedSettingsReadResult))] [JsonSerializable(typeof(MarketplaceAddResult))] [JsonSerializable(typeof(MarketplaceBrowseResult))] [JsonSerializable(typeof(MarketplaceInfo))] @@ -29221,6 +29634,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpUnregisterExternalClientRequest))] [JsonSerializable(typeof(MetadataContextAttributionResult))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttribution))] +[JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionCategories))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionCompactions))] [JsonSerializable(typeof(MetadataContextAttributionResultContextAttributionEntry))] [JsonSerializable(typeof(MetadataContextHeaviestMessagesRequest))] @@ -29272,6 +29686,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(PermissionDecision))] [JsonSerializable(typeof(PermissionDecisionApproveForLocationApproval))] [JsonSerializable(typeof(PermissionDecisionApproveForSessionApproval))] +[JsonSerializable(typeof(PermissionDecisionContext))] [JsonSerializable(typeof(PermissionDecisionRequest))] [JsonSerializable(typeof(PermissionLocationAddToolApprovalParams))] [JsonSerializable(typeof(PermissionLocationApplyParams))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 6b81a4770..47c13846f 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -1353,7 +1353,7 @@ public sealed partial class SessionAutoModeResolvedEvent : SessionEvent public required SessionAutoModeResolvedData Data { get; set; } } -/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. /// Represents the session.managed_settings_resolved event. [Experimental(Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedEvent : SessionEvent @@ -4278,7 +4278,7 @@ public sealed partial class SessionAutoModeResolvedData public bool? StickyOverride { get; set; } } -/// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. [Experimental(Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedData { @@ -4286,7 +4286,12 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("bypassPermissionsDisabled")] public required bool BypassPermissionsDisabled { get; set; } - /// Whether the device (MDM/plist/registry/file) managed-settings layer was present. + /// Whether a session-local permissions layer injected by the SDK host was present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("clientManaged")] + public bool? ClientManaged { get; set; } + + /// Whether an actual device MDM/plist/registry/file managed-settings layer was present. [JsonPropertyName("deviceManaged")] public required bool DeviceManaged { get; set; } @@ -4298,7 +4303,7 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("managedKeys")] public required string[] ManagedKeys { get; set; } - /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("permissionsAllowIntersected")] public bool? PermissionsAllowIntersected { get; set; } @@ -4312,7 +4317,7 @@ public sealed partial class SessionManagedSettingsResolvedData [JsonPropertyName("settings")] public JsonElement? Settings { get; set; } - /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force. + /// Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. [JsonPropertyName("source")] public required ManagedSettingsResolvedSource Source { get; set; } } @@ -8564,6 +8569,11 @@ public sealed partial class SkillsLoadedSkill [JsonPropertyName("argumentHint")] public string? ArgumentHint { get; set; } + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + /// Description of what the skill does. [JsonPropertyName("description")] public required string Description { get; set; } @@ -12142,7 +12152,7 @@ public override void Write(Utf8JsonWriter writer, AutoModeResolvedReasoningBucke } } -/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale). +/// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ManagedSettingsResolvedSource : IEquatable @@ -12161,13 +12171,19 @@ public ManagedSettingsResolvedSource(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + /// Only the server/account channel contributed. public static ManagedSettingsResolvedSource Server { get; } = new("server"); - /// Device-level MDM policy discovered from plist/registry/file (lower authority). + /// Only the device MDM/plist/registry/file channel contributed. public static ManagedSettingsResolvedSource Device { get; } = new("device"); - /// No managed policy is in force (no layer contributed). + /// Only session-local SDK-host injection contributed. + public static ManagedSettingsResolvedSource Client { get; } = new("client"); + + /// More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + public static ManagedSettingsResolvedSource Mixed { get; } = new("mixed"); + + /// No managed policy is in force (no channel contributed). public static ManagedSettingsResolvedSource None { get; } = new("none"); /// Returns a value indicating whether two instances are equivalent. diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 5ecee9f6c..6de48d06c 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -2109,6 +2109,44 @@ type Extension struct { Status ExtensionStatus `json:"status"` } +// Opaque integrator-owned process launch profile for one extension entrypoint. +// Experimental: ExtensionLaunchProfile is part of an experimental API and may change or be +// removed. +type ExtensionLaunchProfile struct { + // Opaque integrator-defined arguments passed to the executable. The runtime does not append + // the extension entrypoint. + Args []string `json:"args"` + // Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, + // SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + Env map[string]string `json:"env"` + // Executable used to launch the extension entrypoint. + Executable string `json:"executable"` +} + +// A discovered extension entrypoint that the registered integrator may classify and resolve +// to an opaque launch profile. +// Experimental: ExtensionLaunchProviderResolveRequest is part of an experimental API and +// may change or be removed. +type ExtensionLaunchProviderResolveRequest struct { + // Source-qualified extension identifier. + ID string `json:"id"` + // Absolute path to the discovered extension entrypoint. + ModulePath string `json:"modulePath"` + // Human-readable extension name. + Name string `json:"name"` + // Discovery source for the extension entrypoint. + Source ExtensionSource `json:"source"` +} + +// The launch profile for a supported entrypoint. Omit launch when the provider does not +// support the entrypoint. +// Experimental: ExtensionLaunchProviderResolveResult is part of an experimental API and may +// change or be removed. +type ExtensionLaunchProviderResolveResult struct { + // Opaque launch profile, omitted when this provider does not support the entrypoint. + Launch *ExtensionLaunchProfile `json:"launch,omitempty"` +} + // Extensions discovered for the session, with their current status. // Experimental: ExtensionList is part of an experimental API and may change or be removed. type ExtensionList struct { @@ -3737,6 +3775,17 @@ type LspInitializeRequest struct { WorkingDirectory *string `json:"workingDirectory,omitempty"` } +// Validated device-managed settings discovered before a session exists. +// Experimental: ManagedSettingsReadResult is part of an experimental API and may change or +// be removed. +type ManagedSettingsReadResult struct { + // Discovery or validation error text when managed settings could not be read safely. + ErrorMessage *string `json:"errorMessage,omitempty"` + // Validated, canonical managed-settings JSON. Omitted when no managed settings were + // discovered or when discovered settings failed validation. + SettingsJSON any `json:"settingsJson,omitempty"` +} + // Result of registering a new marketplace. // Experimental: MarketplaceAddResult is part of an experimental API and may change or be // removed. @@ -6091,10 +6140,26 @@ func (PermissionDecisionApproveForSessionApprovalWrite) Kind() PermissionDecisio return PermissionDecisionApproveForSessionApprovalKindWrite } +// Optional informational context describing how and where the permission decision was made. +// This does not affect permission behavior. +// Experimental: PermissionDecisionContext is part of an experimental API and may change or +// be removed. +type PermissionDecisionContext struct { + // Disposition of the permission request as observed by the responding client. + Outcome PermissionDecisionOutcome `json:"outcome"` + // Controlled reason or actor responsible for the response. + Source PermissionDecisionSource `json:"source"` + // Client surface that submitted the response. + Surface PermissionDecisionSurface `json:"surface"` +} + // Pending permission request ID and the decision to apply (approve/reject and scope). // Experimental: PermissionDecisionRequest is part of an experimental API and may change or // be removed. type PermissionDecisionRequest struct { + // Optional informational context describing how and where this response was made. Omit it + // to preserve legacy behavior without attributing an origin. + DecisionContext *PermissionDecisionContext `json:"decisionContext,omitempty"` // Request ID of the pending permission request RequestID string `json:"requestId"` // The client's response to the pending permission prompt @@ -7852,6 +7917,11 @@ type RegisterEventInterestResult struct { Handle string `json:"handle"` } +// Experimental: RegisterExtensionLaunchProviderResult is part of an experimental API and +// may change or be removed. +type RegisterExtensionLaunchProviderResult struct { +} + // Params to attach an extension loader's tools to a session. // Experimental: RegisterExtensionToolsParams is part of an experimental API and may change // or be removed. @@ -8134,9 +8204,10 @@ type SandboxConfig struct { // toolchains in their default home locations (cargo, go, npm, Maven, and more), plus // read-write access to (and, on Unix, up-front creation of) the scratch caches builds write // on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so - // builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; - // set to false to opt out). - AllowDevToolCaches *bool `json:"allowDevToolCaches,omitempty"` + // builds work without extra configuration; a relocated CARGO_HOME additionally gets its + // Cargo lock files granted read-write. Default: true (enabled by default; set to false to + // opt out). + AllowDevToolAccess *bool `json:"allowDevToolAccess,omitempty"` // Whether sandboxing is enabled for the session. Enabled bool `json:"enabled"` // Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the @@ -8570,6 +8641,8 @@ type ServerSkill struct { // Optional freeform hint describing the skill's expected arguments, from the // `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled (based on global config) @@ -8717,17 +8790,65 @@ type SessionContext struct { // Experimental: SessionContextAttribution is part of an experimental API and may change or // be removed. type SessionContextAttribution struct { + // Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors + // `SessionContextInfo.bufferTokens`. + BufferTokens int64 `json:"bufferTokens"` + // The six normalized `/context` header buckets, computed from the same tokenization as + // `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + // describe window capacity rather than occupied context, so the values do not sum to + // `totalTokens`. + Categories SessionContextAttributionCategories `json:"categories"` // Successful compaction history for the session. Compactions SessionContextAttributionCompactions `json:"compactions"` + // Token count at which background compaction starts. Mirrors + // `SessionContextInfo.compactionThreshold`. + CompactionThreshold int64 `json:"compactionThreshold"` // Flat list of per-source attribution entries. Group by `kind` and render unrecognized // kinds generically. Nesting and rollups are expressed via `parentId`. Entries []SessionContextAttributionEntriesItem `json:"entries"` + // Prompt limit plus the model's output reserve: the full context window + // `categories.freeSpace` and `categories.buffer` are measured against. Mirrors + // `SessionContextInfo.limit`. + Limit int64 `json:"limit"` + // The concrete model id the entire breakdown was tokenized against (feeds the per-model + // token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the + // literal `auto` sentinel, so totals are not undercounted. A single-model approximation of + // a potentially multi-model Auto session. + ModelID string `json:"modelId"` + // How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: + // `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected + // model), `default` (a fallback before any model is known). + ModelSource string `json:"modelSource"` + // Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` + // context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + PromptTokenLimit int64 `json:"promptTokenLimit"` // Total token count of the current context window the entries are measured against (system // message + conversation messages + tool definitions — the same total reported by // /context). Divide an entry's `tokens` by this to derive its share. TotalTokens int64 `json:"totalTokens"` } +// The six normalized `/context` header buckets, computed from the same tokenization as +// `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` +// describe window capacity rather than occupied context, so the values do not sum to +// `totalTokens`. +type SessionContextAttributionCategories struct { + // Output reserve plus post-blocking-threshold buffer. + Buffer int64 `json:"buffer"` + // Custom-instructions tokens (0 when none are configured). + CustomInstructions int64 `json:"customInstructions"` + // Remaining unused window capacity (clamped at 0). + FreeSpace int64 `json:"freeSpace"` + // MCP tool-definition tokens. + MCPTools int64 `json:"mcpTools"` + // Conversation (user/assistant/tool) message tokens. + Messages int64 `json:"messages"` + // System prompt tokens, excluding custom instructions. + SystemPrompt int64 `json:"systemPrompt"` + // Non-MCP tool-definition tokens. + SystemTools int64 `json:"systemTools"` +} + // Successful compaction history for the session. type SessionContextAttributionCompactions struct { // Number of successful compactions in this session. @@ -9424,6 +9545,29 @@ type SessionLoadDeferredRepoHooksResult struct { type SessionLspInitializeResult struct { } +// Enterprise permission policy expressed with the runtime's managed permission-rule syntax. +// Experimental: SessionManagedPermissions is part of an experimental API and may change or +// be removed. +type SessionManagedPermissions struct { + // Permission rules that allow matching operations unless another managed source, deny, or + // ask rule restricts them. + Allow []string `json:"allow,omitzero"` + // Permission rules that require explicit human approval. + Ask []string `json:"ask,omitzero"` + // Permission rules that block matching operations. Deny has highest precedence. + Deny []string `json:"deny,omitzero"` + // When set to `disable`, prevents bypass/allow-all permission modes. + DisableBypassPermissionsMode *DisableBypassPermissionsMode `json:"disableBypassPermissionsMode,omitempty"` +} + +// Managed settings an SDK host may inject at session startup. Only permissions are accepted +// in this initial contract. +// Experimental: SessionManagedSettings is part of an experimental API and may change or be +// removed. +type SessionManagedSettings struct { + Permissions *SessionManagedPermissions `json:"permissions,omitempty"` +} + // Standard MCP CallToolResult // Experimental: SessionMCPAppsCallToolResult is part of an experimental API and may change // or be removed. @@ -9615,6 +9759,9 @@ type SessionOpenOptions struct { DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` // Instruction source IDs disabled for this session. DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` + // MCP server names disabled for this session. Disabled servers are not started or + // authenticated on create or cold resume. + DisabledMCPServers []string `json:"disabledMcpServers,omitzero"` // Skill IDs disabled for this session. DisabledSkills []string `json:"disabledSkills,omitzero"` // Experimental: enable native model citations (Anthropic models today), normalized onto the @@ -9686,6 +9833,9 @@ type SessionOpenOptions struct { LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` // Identifier sent to LSP-style integrations. LspClientName *string `json:"lspClientName,omitempty"` + // Permissions-only enterprise policy injected by the SDK host at session create or resume. + // Composes restrictively with self-fetched and device policy and is not persisted. + ManagedSettings *SessionManagedSettings `json:"managedSettings,omitempty"` // Maximum decoded byte size of a single inline model-facing binary tool result persisted in // session events (default 10 MB). MaxInlineBinaryBytes *int64 `json:"maxInlineBinaryBytes,omitempty"` @@ -10943,6 +11093,8 @@ type Skill struct { // Optional freeform hint describing the skill's expected arguments, from the // `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled @@ -13123,6 +13275,14 @@ const ( DebugCollectLogsSourceShellLog DebugCollectLogsSource = "shell-log" ) +// Experimental: DisableBypassPermissionsMode is part of an experimental API and may change +// or be removed. +type DisableBypassPermissionsMode string + +const ( + DisableBypassPermissionsModeDisable DisableBypassPermissionsMode = "disable" +) + // Effective extension loading and agent-management mode // Experimental: DiscoveredExtensionMode is part of an experimental API and may change or be // removed. @@ -14111,6 +14271,53 @@ const ( PermissionDecisionKindUserNotAvailable PermissionDecisionKind = "user-not-available" ) +// Disposition of a permission request as observed by the responding client. +// Experimental: PermissionDecisionOutcome is part of an experimental API and may change or +// be removed. +type PermissionDecisionOutcome string + +const ( + // The request was approved automatically without a new human decision. + PermissionDecisionOutcomeAutoApproved PermissionDecisionOutcome = "auto_approved" + // The request was denied without an interactive user decision; source records why. + PermissionDecisionOutcomeAutopilotDenied PermissionDecisionOutcome = "autopilot_denied" + // The response came from an interactive user prompt. + PermissionDecisionOutcomePromptedUser PermissionDecisionOutcome = "prompted_user" +) + +// Controlled reason or actor responsible for a permission response. +// Experimental: PermissionDecisionSource is part of an experimental API and may change or +// be removed. +type PermissionDecisionSource string + +const ( + // The host applied a standing policy or override rather than a judge recommendation or + // human decision. + PermissionDecisionSourceHostPolicy PermissionDecisionSource = "host_policy" + // A human supplied the response through an interactive prompt. + PermissionDecisionSourceHumanResponse PermissionDecisionSource = "human_response" + // The response followed the auto-approval judge recommendation. + PermissionDecisionSourceJudgeRecommendation PermissionDecisionSource = "judge_recommendation" + // The host denied the request because no interactive user response was available. + PermissionDecisionSourceUnattendedFallback PermissionDecisionSource = "unattended_fallback" +) + +// Client surface that submitted a permission response. +// Experimental: PermissionDecisionSurface is part of an experimental API and may change or +// be removed. +type PermissionDecisionSurface string + +const ( + // The Copilot App client. + PermissionDecisionSurfaceCopilotApp PermissionDecisionSurface = "copilot_app" + // The non-interactive Copilot CLI prompt mode. + PermissionDecisionSurfacePromptMode PermissionDecisionSurface = "prompt_mode" + // A generic Copilot SDK client. + PermissionDecisionSurfaceSDK PermissionDecisionSurface = "sdk" + // The interactive Copilot CLI terminal UI. + PermissionDecisionSurfaceTui PermissionDecisionSurface = "tui" +) + // Whether the location is a git repo or directory // Experimental: PermissionLocationType is part of an experimental API and may change or be // removed. @@ -15605,6 +15812,29 @@ func (a *ServerLlmInferenceAPI) SetProvider(ctx context.Context) (*LlmInferenceS return &result, nil } +// Experimental: ServerManagedSettingsAPI contains experimental APIs that may change or be +// removed. +type ServerManagedSettingsAPI serverAPI + +// Read discovers device-managed settings from production MDM and managed-file sources, +// validates them against the runtime-owned managed-settings schema, and returns the +// canonical JSON without requiring a session. +// +// RPC method: managedSettings.read. +// +// Returns: Validated device-managed settings discovered before a session exists. +func (a *ServerManagedSettingsAPI) Read(ctx context.Context) (*ManagedSettingsReadResult, error) { + raw, err := a.client.Request(ctx, "managedSettings.read", nil) + if err != nil { + return nil, err + } + var result ManagedSettingsReadResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: ServerMCPAPI contains experimental APIs that may change or be removed. type ServerMCPAPI serverAPI @@ -16747,23 +16977,24 @@ type ServerRPC struct { // Reuse a single struct instead of allocating one for each service on the heap. common serverAPI - Account *ServerAccountAPI - AgentRegistry *ServerAgentRegistryAPI - Agents *ServerAgentsAPI - Commands *ServerCommandsAPI - Extensions *ServerExtensionsAPI - Instructions *ServerInstructionsAPI - LlmInference *ServerLlmInferenceAPI - MCP *ServerMCPAPI - Models *ServerModelsAPI - Plugins *ServerPluginsAPI - Runtime *ServerRuntimeAPI - Secrets *ServerSecretsAPI - SessionFS *ServerSessionFSAPI - Sessions *ServerSessionsAPI - Skills *ServerSkillsAPI - Tools *ServerToolsAPI - User *ServerUserAPI + Account *ServerAccountAPI + AgentRegistry *ServerAgentRegistryAPI + Agents *ServerAgentsAPI + Commands *ServerCommandsAPI + Extensions *ServerExtensionsAPI + Instructions *ServerInstructionsAPI + LlmInference *ServerLlmInferenceAPI + ManagedSettings *ServerManagedSettingsAPI + MCP *ServerMCPAPI + Models *ServerModelsAPI + Plugins *ServerPluginsAPI + Runtime *ServerRuntimeAPI + Secrets *ServerSecretsAPI + SessionFS *ServerSessionFSAPI + Sessions *ServerSessionsAPI + Skills *ServerSkillsAPI + Tools *ServerToolsAPI + User *ServerUserAPI } // Ping checks server responsiveness and returns protocol information. @@ -16787,6 +17018,25 @@ func (a *ServerRPC) Ping(ctx context.Context, params *PingRequest) (*PingResult, return &result, nil } +// RegisterExtensionLaunchProvider registers the calling SDK client as the per-entrypoint +// extension launch provider. Call before creating any sessions. When omitted, the runtime +// temporarily falls back to its built-in Node launcher for backward compatibility. +// +// RPC method: registerExtensionLaunchProvider. +// Experimental: RegisterExtensionLaunchProvider is an experimental API and may change or be +// removed in future versions. +func (a *ServerRPC) RegisterExtensionLaunchProvider(ctx context.Context) (*RegisterExtensionLaunchProviderResult, error) { + raw, err := a.common.client.Request(ctx, "registerExtensionLaunchProvider", nil) + if err != nil { + return nil, err + } + var result RegisterExtensionLaunchProviderResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r := &ServerRPC{} r.common = serverAPI{client: client} @@ -16797,6 +17047,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.Extensions = (*ServerExtensionsAPI)(&r.common) r.Instructions = (*ServerInstructionsAPI)(&r.common) r.LlmInference = (*ServerLlmInferenceAPI)(&r.common) + r.ManagedSettings = (*ServerManagedSettingsAPI)(&r.common) r.MCP = (*ServerMCPAPI)(&r.common) r.Models = (*ServerModelsAPI)(&r.common) r.Plugins = (*ServerPluginsAPI)(&r.common) @@ -19966,6 +20217,9 @@ func (a *PermissionsAPI) GetAllowAll(ctx context.Context) (*AllowAllPermissionSt func (a *PermissionsAPI) HandlePendingPermissionRequest(ctx context.Context, params *PermissionDecisionRequest) (*PermissionRequestResult, error) { req := map[string]any{"sessionId": a.sessionID} if params != nil { + if params.DecisionContext != nil { + req["decisionContext"] = *params.DecisionContext + } req["requestId"] = params.RequestID req["result"] = params.Result } @@ -23936,6 +24190,23 @@ func RegisterClientSessionAPIHandlers(client *jsonrpc2.Client, getHandlers func( }) } +// Experimental: ExtensionLaunchProviderHandler contains experimental APIs that may change +// or be removed. +type ExtensionLaunchProviderHandler interface { + // Resolve asks the registered SDK client to resolve an opaque process launch profile for + // one discovered extension entrypoint immediately before launch or reload. The provider + // must respond within 15 seconds. + // + // RPC method: extensionLaunchProvider.resolve. + // + // Parameters: A discovered extension entrypoint that the registered integrator may classify + // and resolve to an opaque launch profile. + // + // Returns: The launch profile for a supported entrypoint. Omit launch when the provider + // does not support the entrypoint. + Resolve(request *ExtensionLaunchProviderResolveRequest) (*ExtensionLaunchProviderResolveResult, error) +} + // Experimental: GitHubTelemetryHandler contains experimental APIs that may change or be // removed. type GitHubTelemetryHandler interface { @@ -24002,9 +24273,10 @@ type LlmInferenceHandler interface { // Unlike client-session handlers these carry no implicit session id dispatch // key; a single set of handlers serves the entire connection. type ClientGlobalAPIHandlers struct { - GitHubTelemetry GitHubTelemetryHandler - Hooks HooksHandler - LlmInference LlmInferenceHandler + ExtensionLaunchProvider ExtensionLaunchProviderHandler + GitHubTelemetry GitHubTelemetryHandler + Hooks HooksHandler + LlmInference LlmInferenceHandler } func clientGlobalHandlerError(err error) *jsonrpc2.Error { @@ -24021,6 +24293,24 @@ func clientGlobalHandlerError(err error) *jsonrpc2.Error { // RegisterClientGlobalAPIHandlers registers handlers for server-to-client client-global API // calls. func RegisterClientGlobalAPIHandlers(client *jsonrpc2.Client, handlers *ClientGlobalAPIHandlers) { + client.SetRequestHandler("extensionLaunchProvider.resolve", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request ExtensionLaunchProviderResolveRequest + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("Invalid params: %v", err)} + } + if handlers == nil || handlers.ExtensionLaunchProvider == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: "No extensionLaunchProvider client-global handler registered"} + } + result, err := handlers.ExtensionLaunchProvider.Resolve(&request) + if err != nil { + return nil, clientGlobalHandlerError(err) + } + raw, err := json.Marshal(result) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil + }) client.SetRequestHandler("gitHubTelemetry.event", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { var request GitHubTelemetryNotification if err := json.Unmarshal(params, &request); err != nil { diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index fdc76729b..8b69c28c2 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -2683,13 +2683,15 @@ func (r PermissionDecisionUserNotAvailable) MarshalJSON() ([]byte, error) { func (r *PermissionDecisionRequest) UnmarshalJSON(data []byte) error { type rawPermissionDecisionRequest struct { - RequestID string `json:"requestId"` - Result json.RawMessage `json:"result"` + DecisionContext *PermissionDecisionContext `json:"decisionContext,omitempty"` + RequestID string `json:"requestId"` + Result json.RawMessage `json:"result"` } var raw rawPermissionDecisionRequest if err := json.Unmarshal(data, &raw); err != nil { return err } + r.DecisionContext = raw.DecisionContext r.RequestID = raw.RequestID if raw.Result != nil { value, err := unmarshalPermissionDecision(raw.Result) @@ -3711,6 +3713,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { DetachedFromSpawningParentEngagementID *string `json:"detachedFromSpawningParentEngagementId,omitempty"` DetachedFromSpawningParentSessionID *string `json:"detachedFromSpawningParentSessionId,omitempty"` DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` + DisabledMCPServers []string `json:"disabledMcpServers,omitzero"` DisabledSkills []string `json:"disabledSkills,omitzero"` EnableCitations *bool `json:"enableCitations,omitempty"` EnableFileChangeTracking *bool `json:"enableFileChangeTracking,omitempty"` @@ -3731,6 +3734,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` LogInteractiveShells *bool `json:"logInteractiveShells,omitempty"` LspClientName *string `json:"lspClientName,omitempty"` + ManagedSettings *SessionManagedSettings `json:"managedSettings,omitempty"` MaxInlineBinaryBytes *int64 `json:"maxInlineBinaryBytes,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Model *string `json:"model,omitempty"` @@ -3787,6 +3791,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.DetachedFromSpawningParentEngagementID = raw.DetachedFromSpawningParentEngagementID r.DetachedFromSpawningParentSessionID = raw.DetachedFromSpawningParentSessionID r.DisabledInstructionSources = raw.DisabledInstructionSources + r.DisabledMCPServers = raw.DisabledMCPServers r.DisabledSkills = raw.DisabledSkills r.EnableCitations = raw.EnableCitations r.EnableFileChangeTracking = raw.EnableFileChangeTracking @@ -3807,6 +3812,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.IsExperimentalMode = raw.IsExperimentalMode r.LogInteractiveShells = raw.LogInteractiveShells r.LspClientName = raw.LspClientName + r.ManagedSettings = raw.ManagedSettings r.MaxInlineBinaryBytes = raw.MaxInlineBinaryBytes r.Memory = raw.Memory r.Model = raw.Model diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 26e7b5f72..0034af6a7 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -649,24 +649,26 @@ func (*PendingMessagesModifiedData) Type() SessionEventType { return SessionEventTypePendingMessagesModified } -// Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +// Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. // Experimental: SessionManagedSettingsResolvedData is part of an experimental API and may change or be removed. type SessionManagedSettingsResolvedData struct { // Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. BypassPermissionsDisabled bool `json:"bypassPermissionsDisabled"` - // Whether the device (MDM/plist/registry/file) managed-settings layer was present + // Whether a session-local permissions layer injected by the SDK host was present + ClientManaged *bool `json:"clientManaged,omitempty"` + // Whether an actual device MDM/plist/registry/file managed-settings layer was present DeviceManaged bool `json:"deviceManaged"` // Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. FailClosed bool `json:"failClosed"` // The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. ManagedKeys []string `json:"managedKeys"` - // Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + // Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. PermissionsAllowIntersected *bool `json:"permissionsAllowIntersected,omitempty"` // Whether the server (account/org) managed-settings layer was present ServerManaged bool `json:"serverManaged"` // The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. Settings any `json:"settings,omitempty"` - // Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + // Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. Source ManagedSettingsResolvedSource `json:"source"` } @@ -3571,6 +3573,8 @@ type ShutdownTokenDetail struct { type SkillsLoadedSkill struct { // Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field ArgumentHint *string `json:"argumentHint,omitempty"` + // Canonical slash command name used to invoke the skill, without the leading '/' + CommandName *string `json:"commandName,omitempty"` // Description of what the skill does Description string `json:"description"` // Whether the skill is currently enabled @@ -4354,15 +4358,19 @@ const ( ManagedSettingsEnforcedEscalationUnrestrictedURLs ManagedSettingsEnforcedEscalation = "unrestricted_urls" ) -// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. type ManagedSettingsResolvedSource string const ( - // Device-level MDM policy discovered from plist/registry/file (lower authority). + // Only session-local SDK-host injection contributed. + ManagedSettingsResolvedSourceClient ManagedSettingsResolvedSource = "client" + // Only the device MDM/plist/registry/file channel contributed. ManagedSettingsResolvedSourceDevice ManagedSettingsResolvedSource = "device" - // No managed policy is in force (no layer contributed). + // More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + ManagedSettingsResolvedSourceMixed ManagedSettingsResolvedSource = "mixed" + // No managed policy is in force (no channel contributed). ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSource = "none" - // Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + // Only the server/account channel contributed. ManagedSettingsResolvedSourceServer ManagedSettingsResolvedSource = "server" ) diff --git a/go/zsession_events.go b/go/zsession_events.go index fbc100277..48ad42849 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -469,7 +469,9 @@ const ( ManagedSettingsEnforcedEscalationAutoApproval = rpc.ManagedSettingsEnforcedEscalationAutoApproval ManagedSettingsEnforcedEscalationUnrestrictedPaths = rpc.ManagedSettingsEnforcedEscalationUnrestrictedPaths ManagedSettingsEnforcedEscalationUnrestrictedURLs = rpc.ManagedSettingsEnforcedEscalationUnrestrictedURLs + ManagedSettingsResolvedSourceClient = rpc.ManagedSettingsResolvedSourceClient ManagedSettingsResolvedSourceDevice = rpc.ManagedSettingsResolvedSourceDevice + ManagedSettingsResolvedSourceMixed = rpc.ManagedSettingsResolvedSourceMixed ManagedSettingsResolvedSourceNone = rpc.ManagedSettingsResolvedSourceNone ManagedSettingsResolvedSourceServer = rpc.ManagedSettingsResolvedSourceServer MCPHeadersRefreshCompletedOutcomeHeaders = rpc.MCPHeadersRefreshCompletedOutcomeHeaders diff --git a/java/pom.xml b/java/pom.xml index fcdd9cc21..8080e2c49 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.78 + ^1.0.79-5 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index c4a159b6e..bd41d4b12 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 7547468e8..3e0e5af6a 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java index 1ffa10063..32386f898 100644 --- a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java +++ b/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + * Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. * * @since 1.0.0 */ @@ -20,6 +20,10 @@ public enum ManagedSettingsResolvedSource { SERVER("server"), /** The {@code device} variant. */ DEVICE("device"), + /** The {@code client} variant. */ + CLIENT("client"), + /** The {@code mixed} variant. */ + MIXED("mixed"), /** The {@code none} variant. */ NONE("none"); diff --git a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java index 7c7a4df73..f935f4462 100644 --- a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) @@ -35,17 +35,19 @@ public final class SessionManagedSettingsResolvedEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionManagedSettingsResolvedEventData( - /** Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force */ + /** Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. */ @JsonProperty("source") ManagedSettingsResolvedSource source, /** Whether the server (account/org) managed-settings layer was present */ @JsonProperty("serverManaged") Boolean serverManaged, - /** Whether the device (MDM/plist/registry/file) managed-settings layer was present */ + /** Whether an actual device MDM/plist/registry/file managed-settings layer was present */ @JsonProperty("deviceManaged") Boolean deviceManaged, + /** Whether a session-local permissions layer injected by the SDK host was present */ + @JsonProperty("clientManaged") Boolean clientManaged, /** Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. */ @JsonProperty("failClosed") Boolean failClosed, /** Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. */ @JsonProperty("bypassPermissionsDisabled") Boolean bypassPermissionsDisabled, - /** Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ + /** Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ @JsonProperty("permissionsAllowIntersected") Boolean permissionsAllowIntersected, /** The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. */ @JsonProperty("managedKeys") List managedKeys, diff --git a/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java b/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java index d3196c6bb..932d9affe 100644 --- a/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java +++ b/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java @@ -23,6 +23,8 @@ public record SkillsLoadedSkill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java b/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java new file mode 100644 index 000000000..1e6b1e7db --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum DisableBypassPermissionsMode { + /** The {@code disable} variant. */ + DISABLE("disable"); + + private final String value; + DisableBypassPermissionsMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static DisableBypassPermissionsMode fromValue(String value) { + for (DisableBypassPermissionsMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown DisableBypassPermissionsMode value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java new file mode 100644 index 000000000..e7590c7f9 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Opaque integrator-owned process launch profile for one extension entrypoint. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProfile( + /** Executable used to launch the extension entrypoint. */ + @JsonProperty("executable") String executable, + /** Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. */ + @JsonProperty("args") List args, + /** Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. */ + @JsonProperty("env") Map env +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java new file mode 100644 index 000000000..7b520f906 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProviderResolveRequest( + /** Source-qualified extension identifier. */ + @JsonProperty("id") String id, + /** Human-readable extension name. */ + @JsonProperty("name") String name, + /** Absolute path to the discovered extension entrypoint. */ + @JsonProperty("modulePath") String modulePath, + /** Discovery source for the extension entrypoint. */ + @JsonProperty("source") ExtensionSource source +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java new file mode 100644 index 000000000..8a43ad4af --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ExtensionLaunchProviderResolveResult( + /** Opaque launch profile, omitted when this provider does not support the entrypoint. */ + @JsonProperty("launch") ExtensionLaunchProfile launch +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java new file mode 100644 index 000000000..2018f62ce --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Validated device-managed settings discovered before a session exists. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ManagedSettingsReadResult( + /** Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. */ + @JsonProperty("settingsJson") Object settingsJson, + /** Discovery or validation error text when managed settings could not be read safely. */ + @JsonProperty("errorMessage") String errorMessage +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java new file mode 100644 index 000000000..73934eea6 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record PermissionDecisionContext( + /** Disposition of the permission request as observed by the responding client. */ + @JsonProperty("outcome") PermissionDecisionOutcome outcome, + /** Controlled reason or actor responsible for the response. */ + @JsonProperty("source") PermissionDecisionSource source, + /** Client surface that submitted the response. */ + @JsonProperty("surface") PermissionDecisionSurface surface +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java new file mode 100644 index 000000000..d46c460a2 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Disposition of a permission request as observed by the responding client. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionOutcome { + /** The {@code auto_approved} variant. */ + AUTO_APPROVED("auto_approved"), + /** The {@code autopilot_denied} variant. */ + AUTOPILOT_DENIED("autopilot_denied"), + /** The {@code prompted_user} variant. */ + PROMPTED_USER("prompted_user"); + + private final String value; + PermissionDecisionOutcome(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionOutcome fromValue(String value) { + for (PermissionDecisionOutcome v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionOutcome value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java new file mode 100644 index 000000000..ee807b095 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Controlled reason or actor responsible for a permission response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionSource { + /** The {@code judge_recommendation} variant. */ + JUDGE_RECOMMENDATION("judge_recommendation"), + /** The {@code human_response} variant. */ + HUMAN_RESPONSE("human_response"), + /** The {@code host_policy} variant. */ + HOST_POLICY("host_policy"), + /** The {@code unattended_fallback} variant. */ + UNATTENDED_FALLBACK("unattended_fallback"); + + private final String value; + PermissionDecisionSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionSource fromValue(String value) { + for (PermissionDecisionSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionSource value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java new file mode 100644 index 000000000..2cf634879 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Client surface that submitted a permission response. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum PermissionDecisionSurface { + /** The {@code tui} variant. */ + TUI("tui"), + /** The {@code prompt_mode} variant. */ + PROMPT_MODE("prompt_mode"), + /** The {@code copilot_app} variant. */ + COPILOT_APP("copilot_app"), + /** The {@code sdk} variant. */ + SDK("sdk"); + + private final String value; + PermissionDecisionSurface(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static PermissionDecisionSurface fromValue(String value) { + for (PermissionDecisionSurface v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown PermissionDecisionSurface value: " + value); + } +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java index be4f2abe2..cfb2c23ff 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java @@ -31,7 +31,7 @@ public record SandboxConfig( @JsonProperty("gitAuth") Boolean gitAuth, /** Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ @JsonProperty("ghAuth") Boolean ghAuth, - /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). */ - @JsonProperty("allowDevToolCaches") Boolean allowDevToolCaches + /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ + @JsonProperty("allowDevToolAccess") Boolean allowDevToolAccess ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java new file mode 100644 index 000000000..e85b7b987 --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code managedSettings} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerManagedSettingsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerManagedSettingsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Validated device-managed settings discovered before a session exists. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture read() { + return caller.invoke("managedSettings.read", java.util.Map.of(), ManagedSettingsReadResult.class); + } + +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index e66f32737..c01545a18 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java @@ -49,6 +49,8 @@ public final class ServerRpc { public final ServerCommandsApi commands; /** API methods for the {@code user} namespace. */ public final ServerUserApi user; + /** API methods for the {@code managedSettings} namespace. */ + public final ServerManagedSettingsApi managedSettings; /** API methods for the {@code runtime} namespace. */ public final ServerRuntimeApi runtime; /** API methods for the {@code sessionFs} namespace. */ @@ -79,6 +81,7 @@ public ServerRpc(RpcCaller caller) { this.instructions = new ServerInstructionsApi(caller); this.commands = new ServerCommandsApi(caller); this.user = new ServerUserApi(caller); + this.managedSettings = new ServerManagedSettingsApi(caller); this.runtime = new ServerRuntimeApi(caller); this.sessionFs = new ServerSessionFsApi(caller); this.llmInference = new ServerLlmInferenceApi(caller); @@ -108,4 +111,15 @@ public CompletableFuture connect(ConnectParams params) { return caller.invoke("connect", params, ConnectResult.class); } + /** + * Invokes {@code registerExtensionLaunchProvider}. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture registerExtensionLaunchProvider() { + return caller.invoke("registerExtensionLaunchProvider", java.util.Map.of(), Void.class); + } + } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java index 349824526..b1d409d9d 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java @@ -23,6 +23,8 @@ public record ServerSkill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java new file mode 100644 index 000000000..79698b27c --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Enterprise permission policy expressed with the runtime's managed permission-rule syntax. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionManagedPermissions( + /** When set to `disable`, prevents bypass/allow-all permission modes. */ + @JsonProperty("disableBypassPermissionsMode") DisableBypassPermissionsMode disableBypassPermissionsMode, + /** Permission rules that block matching operations. Deny has highest precedence. */ + @JsonProperty("deny") List deny, + /** Permission rules that require explicit human approval. */ + @JsonProperty("ask") List ask, + /** Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. */ + @JsonProperty("allow") List allow +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java new file mode 100644 index 000000000..ddba69d8b --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionManagedSettings( + @JsonProperty("permissions") SessionManagedPermissions permissions +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java index ef7fba44d..c27f37afb 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java @@ -36,12 +36,47 @@ public record SessionMetadataGetContextAttributionResult( public record SessionMetadataGetContextAttributionResultContextAttribution( /** Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. */ @JsonProperty("totalTokens") Long totalTokens, + /** The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. */ + @JsonProperty("modelId") String modelId, + /** How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). */ + @JsonProperty("modelSource") String modelSource, + /** Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. */ + @JsonProperty("promptTokenLimit") Long promptTokenLimit, + /** Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. */ + @JsonProperty("limit") Long limit, + /** Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. */ + @JsonProperty("bufferTokens") Long bufferTokens, + /** Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. */ + @JsonProperty("compactionThreshold") Long compactionThreshold, + /** The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. */ + @JsonProperty("categories") SessionMetadataGetContextAttributionResultContextAttributionCategories categories, /** Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. */ @JsonProperty("entries") List entries, /** Successful compaction history for the session. */ @JsonProperty("compactions") SessionMetadataGetContextAttributionResultContextAttributionCompactions compactions ) { + /** The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionMetadataGetContextAttributionResultContextAttributionCategories( + /** System prompt tokens, excluding custom instructions. */ + @JsonProperty("systemPrompt") Long systemPrompt, + /** Custom-instructions tokens (0 when none are configured). */ + @JsonProperty("customInstructions") Long customInstructions, + /** Non-MCP tool-definition tokens. */ + @JsonProperty("systemTools") Long systemTools, + /** MCP tool-definition tokens. */ + @JsonProperty("mcpTools") Long mcpTools, + /** Conversation (user/assistant/tool) message tokens. */ + @JsonProperty("messages") Long messages, + /** Remaining unused window capacity (clamped at 0). */ + @JsonProperty("freeSpace") Long freeSpace, + /** Output reserve plus post-blocking-threshold buffer. */ + @JsonProperty("buffer") Long buffer + ) { + } + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionMetadataGetContextAttributionResultContextAttributionEntriesItem( diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java index a8108e829..cf253bff0 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java @@ -47,6 +47,8 @@ public record SessionOpenOptions( @JsonProperty("expAssignments") Object expAssignments, /** Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap. */ @JsonProperty("enableManagedSettings") Boolean enableManagedSettings, + /** Permissions-only enterprise policy injected by the SDK host at session create or resume. Composes restrictively with self-fetched and device policy and is not persisted. */ + @JsonProperty("managedSettings") SessionManagedSettings managedSettings, /** Opt in to capturing file changes for session rewind and session diff. Capture cannot reconstruct changes made before it was enabled. On create it starts capture from the first turn. It is also honored on resume: for a session that already has tracked prior turns, tracking continues automatically even if this is omitted; passing it on resume additionally enables tracking for an eligible session that has no prior root turn yet. Resuming a session whose prior root turns were never tracked has no restorable baseline, so tracking stays disabled for it and rewind reports file change tracking as unavailable; the resume itself still succeeds, so sessions that predate tracking remain loadable. The opt-in is only rejected when the session can never track (a subagent session, or one without local session storage). It is intentionally absent from the mutable options update because enabling it after edits have occurred would create an incomplete, misleading baseline. Subagents share the parent session's capture store and are not tracked as separate rewind points: a file a subagent writes is attributed to whichever root user turn was open when the capture was staged, just before the tool body ran. A turn cannot open while a staged capture is still in flight, so a subagent tool that staged under the spawning turn stays attributed to it however late the write lands, while a capture it stages after the user's next message belongs to that later turn. Attribution decides which turn's rewind point counts and file preview include that write; it does not narrow which rewinds revert it, because a rewind restores every capture from the selected turn onward, so the earlier spawning turn reverts it as well. */ @JsonProperty("enableFileChangeTracking") Boolean enableFileChangeTracking, /** Feature-flag values resolved by the host. */ @@ -101,6 +103,8 @@ public record SessionOpenOptions( @JsonProperty("logInteractiveShells") Boolean logInteractiveShells, /** How MCP server environment values are interpreted. */ @JsonProperty("envValueMode") SessionOpenOptionsEnvValueMode envValueMode, + /** MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. */ + @JsonProperty("disabledMcpServers") List disabledMcpServers, /** Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @JsonProperty("allowAllMcpServerInstructions") Boolean allowAllMcpServerInstructions, /** Additional directories to search for skills. */ diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java index 075dcee26..a4d2ba67e 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java @@ -29,6 +29,8 @@ public record SessionPermissionsHandlePendingPermissionRequestParams( /** Request ID of the pending permission request */ @JsonProperty("requestId") String requestId, /** The client's response to the pending permission prompt */ - @JsonProperty("result") Object result + @JsonProperty("result") Object result, + /** Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. */ + @JsonProperty("decisionContext") PermissionDecisionContext decisionContext ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java b/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java index 4ad411687..88e7eb116 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java @@ -23,6 +23,8 @@ public record Skill( /** Unique identifier for the skill */ @JsonProperty("name") String name, + /** Canonical slash command name used to invoke the skill, without the leading '/' */ + @JsonProperty("commandName") String commandName, /** Description of what the skill does */ @JsonProperty("description") String description, /** Source location type (e.g., project, personal-copilot, plugin, builtin) */ diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/src/main/java/com/github/copilot/CopilotSession.java index b977b69a1..4683fdf01 100644 --- a/java/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/src/main/java/com/github/copilot/CopilotSession.java @@ -1025,8 +1025,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques return; } getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, - result)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, result, + null)); } catch (Exception e) { LOG.log(Level.WARNING, "Error sending permission result for requestId=" + requestId, e); } @@ -1036,8 +1036,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques PermissionRequestResult denied = new PermissionRequestResult(); denied.setKind(PermissionRequestResultKind.DENIED_COULD_NOT_REQUEST_FROM_USER); getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, - denied)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied, + null)); } catch (Exception e) { LOG.log(Level.WARNING, "Error sending permission denied for requestId=" + requestId, e); } @@ -1049,7 +1049,8 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques PermissionRequestResult denied = new PermissionRequestResult(); denied.setKind(PermissionRequestResultKind.DENIED_COULD_NOT_REQUEST_FROM_USER); getRpc().permissions.handlePendingPermissionRequest( - new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied)); + new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, denied, + null)); } catch (Exception sendEx) { LOG.log(Level.WARNING, "Error sending permission denied for requestId=" + requestId, sendEx); } diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java index f3b3f9808..e92b0f968 100644 --- a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java +++ b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java @@ -498,7 +498,7 @@ void sessionRpc_permissions_handlePendingPermissionRequest_merges_sessionId() { var stub = new StubCaller(); var session = new SessionRpc(stub, "sess-perm"); - var permParams = new SessionPermissionsHandlePendingPermissionRequestParams(null, "req-perm-1", "allow"); + var permParams = new SessionPermissionsHandlePendingPermissionRequestParams(null, "req-perm-1", "allow", null); session.permissions.handlePendingPermissionRequest(permParams); assertEquals(1, stub.calls.size()); diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 3b8a716c7..80d224bbc 100644 --- a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -338,10 +338,11 @@ void sessionModelSwitchToParams_record() { @Test void sessionPermissionsHandlePendingPermissionRequestParams_record() { - var params = new SessionPermissionsHandlePendingPermissionRequestParams("sess-33", "req-1", "allow"); + var params = new SessionPermissionsHandlePendingPermissionRequestParams("sess-33", "req-1", "allow", null); assertEquals("sess-33", params.sessionId()); assertEquals("req-1", params.requestId()); assertEquals("allow", params.result()); + assertNull(params.decisionContext()); } @Test @@ -706,11 +707,12 @@ void sessionShellKillResult_record() { @Test void sessionSkillsListResult_nested() { - var item = new Skill("deploy", "Deploy the app", SkillSource.PROJECT, true, true, "/skills/deploy.md", null, - null); + var item = new Skill("deploy", "deploy", "Deploy the app", SkillSource.PROJECT, true, true, "/skills/deploy.md", + null, null); var result = new SessionSkillsListResult(List.of(item)); assertEquals(1, result.skills().size()); assertEquals("deploy", result.skills().get(0).name()); + assertEquals("deploy", result.skills().get(0).commandName()); assertEquals(SkillSource.PROJECT, result.skills().get(0).source()); assertTrue(result.skills().get(0).enabled()); } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index b3c43e927..2416d147c 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index ca259556b..3aa1a72c7 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index aed2d4465..7a559a612 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index cdd3b29eb..7a8c66909 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -418,6 +418,9 @@ export type DebugCollectLogsResultKind = | "archive" /** A directory containing redacted files was written. */ | "directory"; + +/** @experimental */ +export type DisableBypassPermissionsMode = "disable"; /** * Persisted extension discovery source * @@ -1271,6 +1274,63 @@ export type SessionContextAttribution = { * Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. */ totalTokens: number; + /** + * The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + */ + modelId: string; + /** + * How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + */ + modelSource: string; + /** + * Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + */ + promptTokenLimit: number; + /** + * Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + */ + limit: number; + /** + * Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + */ + bufferTokens: number; + /** + * Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + */ + compactionThreshold: number; + /** + * The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + */ + categories: { + /** + * System prompt tokens, excluding custom instructions. + */ + systemPrompt: number; + /** + * Custom-instructions tokens (0 when none are configured). + */ + customInstructions: number; + /** + * Non-MCP tool-definition tokens. + */ + systemTools: number; + /** + * MCP tool-definition tokens. + */ + mcpTools: number; + /** + * Conversation (user/assistant/tool) message tokens. + */ + messages: number; + /** + * Remaining unused window capacity (clamped at 0). + */ + freeSpace: number; + /** + * Output reserve plus post-blocking-threshold buffer. + */ + buffer: number; + }; /** * Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. */ @@ -1619,6 +1679,52 @@ export type PermissionDecisionApproveForLocationApproval = | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalFactory | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess; +/** + * Disposition of a permission request as observed by the responding client. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionOutcome". + */ +/** @experimental */ +export type PermissionDecisionOutcome = + /** The request was approved automatically without a new human decision. */ + | "auto_approved" + /** The request was denied without an interactive user decision; source records why. */ + | "autopilot_denied" + /** The response came from an interactive user prompt. */ + | "prompted_user"; +/** + * Controlled reason or actor responsible for a permission response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionSource". + */ +/** @experimental */ +export type PermissionDecisionSource = + /** The response followed the auto-approval judge recommendation. */ + | "judge_recommendation" + /** A human supplied the response through an interactive prompt. */ + | "human_response" + /** The host applied a standing policy or override rather than a judge recommendation or human decision. */ + | "host_policy" + /** The host denied the request because no interactive user response was available. */ + | "unattended_fallback"; +/** + * Client surface that submitted a permission response. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionSurface". + */ +/** @experimental */ +export type PermissionDecisionSurface = + /** The interactive Copilot CLI terminal UI. */ + | "tui" + /** The non-interactive Copilot CLI prompt mode. */ + | "prompt_mode" + /** The Copilot App client. */ + | "copilot_app" + /** A generic Copilot SDK client. */ + | "sdk"; /** * Tool approval to persist and apply * @@ -5221,6 +5327,61 @@ export interface ExtensionContextPushInput { [k: string]: unknown | undefined; }; } +/** + * Opaque integrator-owned process launch profile for one extension entrypoint. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProfile". + */ +/** @experimental */ +export interface ExtensionLaunchProfile { + /** + * Executable used to launch the extension entrypoint. + */ + executable: string; + /** + * Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + */ + args: string[]; + /** + * Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + */ + env: { + [k: string]: string | undefined; + }; +} +/** + * A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProviderResolveRequest". + */ +/** @experimental */ +export interface ExtensionLaunchProviderResolveRequest { + /** + * Source-qualified extension identifier. + */ + id: string; + /** + * Human-readable extension name. + */ + name: string; + /** + * Absolute path to the discovered extension entrypoint. + */ + modulePath: string; + source: ExtensionSource; +} +/** + * The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ExtensionLaunchProviderResolveResult". + */ +/** @experimental */ +export interface ExtensionLaunchProviderResolveResult { + launch?: ExtensionLaunchProfile; +} /** * Extensions discovered for the session, with their current status. * @@ -7347,6 +7508,25 @@ export interface LspInitializeRequest { */ force?: boolean; } +/** + * Validated device-managed settings discovered before a session exists. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "ManagedSettingsReadResult". + */ +/** @experimental */ +export interface ManagedSettingsReadResult { + /** + * Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + */ + settingsJson?: { + [k: string]: unknown | undefined; + }; + /** + * Discovery or validation error text when managed settings could not be read safely. + */ + errorMessage?: string; +} /** * Result of registering a new marketplace. * @@ -10351,6 +10531,18 @@ export interface PermissionDecisionDeniedByPermissionRequestHook { */ interrupt?: boolean; } +/** + * Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "PermissionDecisionContext". + */ +/** @experimental */ +export interface PermissionDecisionContext { + outcome: PermissionDecisionOutcome; + source: PermissionDecisionSource; + surface: PermissionDecisionSurface; +} /** * Pending permission request ID and the decision to apply (approve/reject and scope). * @@ -10364,6 +10556,7 @@ export interface PermissionDecisionRequest { */ requestId: string; result: PermissionDecision; + decisionContext?: PermissionDecisionContext; } /** * Location-scoped tool approval to persist. @@ -13056,9 +13249,9 @@ export interface SandboxConfig { */ ghAuth?: boolean; /** - * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ - allowDevToolCaches?: boolean; + allowDevToolAccess?: boolean; } /** * User-managed sandbox policy fragment merged into the auto-discovered base policy. @@ -13663,6 +13856,10 @@ export interface ServerSkill { * Unique identifier for the skill */ name: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ @@ -14477,6 +14674,38 @@ export interface SessionLoadDeferredRepoHooksResult { */ hookCount: number; } +/** + * Enterprise permission policy expressed with the runtime's managed permission-rule syntax. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionManagedPermissions". + */ +/** @experimental */ +export interface SessionManagedPermissions { + disableBypassPermissionsMode?: DisableBypassPermissionsMode; + /** + * Permission rules that block matching operations. Deny has highest precedence. + */ + deny?: string[]; + /** + * Permission rules that require explicit human approval. + */ + ask?: string[]; + /** + * Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. + */ + allow?: string[]; +} +/** + * Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SessionManagedSettings". + */ +/** @experimental */ +export interface SessionManagedSettings { + permissions?: SessionManagedPermissions; +} /** * Point-in-time snapshot of slow-changing session identifier and state fields * @@ -14628,6 +14857,7 @@ export interface SessionOpenOptions { * Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap. */ enableManagedSettings?: boolean; + managedSettings?: SessionManagedSettings; /** * Opt in to capturing file changes for session rewind and session diff. Capture cannot reconstruct changes made before it was enabled. On create it starts capture from the first turn. It is also honored on resume: for a session that already has tracked prior turns, tracking continues automatically even if this is omitted; passing it on resume additionally enables tracking for an eligible session that has no prior root turn yet. Resuming a session whose prior root turns were never tracked has no restorable baseline, so tracking stays disabled for it and rewind reports file change tracking as unavailable; the resume itself still succeeds, so sessions that predate tracking remain loadable. The opt-in is only rejected when the session can never track (a subagent session, or one without local session storage). It is intentionally absent from the mutable options update because enabling it after edits have occurred would create an incomplete, misleading baseline. Subagents share the parent session's capture store and are not tracked as separate rewind points: a file a subagent writes is attributed to whichever root user turn was open when the capture was staged, just before the tool body ran. A turn cannot open while a staged capture is still in flight, so a subagent tool that staged under the spawning turn stays attributed to it however late the write lands, while a capture it stages after the user's next message belongs to that later turn. Attribution decides which turn's rewind point counts and file preview include that write; it does not narrow which rewinds revert it, because a rewind restores every capture from the selected turn onward, so the earlier spawning turn reverts it as well. */ @@ -14722,6 +14952,10 @@ export interface SessionOpenOptions { */ logInteractiveShells?: boolean; envValueMode?: SessionOpenOptionsEnvValueMode; + /** + * MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. + */ + disabledMcpServers?: string[]; /** * Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. */ @@ -16203,6 +16437,10 @@ export interface Skill { * Unique identifier for the skill */ name: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ @@ -18718,6 +18956,13 @@ export function createServerRpc(connection: MessageConnection) { disable: async (params: DiscoveredExtensionsDisableRequest): Promise => connection.sendRequest("extensions.disable", params), }, + /** + * Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + * + * @experimental + */ + registerExtensionLaunchProvider: async (): Promise => + connection.sendRequest("registerExtensionLaunchProvider", {}), /** @experimental */ plugins: { /** @@ -18931,6 +19176,16 @@ export function createServerRpc(connection: MessageConnection) { }, }, /** @experimental */ + managedSettings: { + /** + * Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + * + * @returns Validated device-managed settings discovered before a session exists. + */ + read: async (): Promise => + connection.sendRequest("managedSettings.read", {}), + }, + /** @experimental */ runtime: { /** * Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. @@ -21646,6 +21901,19 @@ export function registerClientSessionApiHandlers( }); } +/** Handler for `extensionLaunchProvider` client global API methods. */ +/** @experimental */ +export interface ExtensionLaunchProviderHandler { + /** + * Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds. + * + * @param params A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. + * + * @returns The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. + */ + resolve(params: ExtensionLaunchProviderResolveRequest): Promise; +} + /** Handler for `llmInference` client global API methods. */ /** @experimental */ export interface LlmInferenceHandler { @@ -21680,6 +21948,7 @@ export interface GitHubTelemetryHandler { /** All client global API handler groups. */ export interface ClientGlobalApiHandlers { + extensionLaunchProvider?: ExtensionLaunchProviderHandler; llmInference?: LlmInferenceHandler; gitHubTelemetry?: GitHubTelemetryHandler; } @@ -21695,6 +21964,11 @@ export function registerClientGlobalApiHandlers( connection: MessageConnection, handlers: ClientGlobalApiHandlers, ): void { + connection.onRequest("extensionLaunchProvider.resolve", async (params: ExtensionLaunchProviderResolveRequest) => { + const handler = handlers.extensionLaunchProvider; + if (!handler) throw new Error("No extensionLaunchProvider client-global handler registered"); + return handler.resolve(params); + }); connection.onRequest("llmInference.httpRequestStart", async (params: LlmInferenceHttpRequestStartRequest) => { const handler = handlers.llmInference; if (!handler) throw new Error("No llmInference client-global handler registered"); diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 1dad4ab16..19d044d6d 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -742,14 +742,18 @@ export type AutoModeResolvedReasoningBucket = /** The request looks high-reasoning; a stronger model is appropriate. */ | "high"; /** - * Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) + * Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. */ export type ManagedSettingsResolvedSource = - /** Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). */ + /** Only the server/account channel contributed. */ | "server" - /** Device-level MDM policy discovered from plist/registry/file (lower authority). */ + /** Only the device MDM/plist/registry/file channel contributed. */ | "device" - /** No managed policy is in force (no layer contributed). */ + /** Only session-local SDK-host injection contributed. */ + | "client" + /** More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. */ + | "mixed" + /** No managed policy is in force (no channel contributed). */ | "none"; /** * The category of runtime action that enterprise managed settings governed (blocked or capped) @@ -8644,7 +8648,7 @@ export interface AutoModeResolvedData { stickyOverride?: boolean; } /** - * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. */ /** @experimental */ export interface ManagedSettingsResolvedEvent { @@ -8675,7 +8679,7 @@ export interface ManagedSettingsResolvedEvent { type: "session.managed_settings_resolved"; } /** - * Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. + * Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. */ /** @experimental */ export interface ManagedSettingsResolvedData { @@ -8684,7 +8688,11 @@ export interface ManagedSettingsResolvedData { */ bypassPermissionsDisabled: boolean; /** - * Whether the device (MDM/plist/registry/file) managed-settings layer was present + * Whether a session-local permissions layer injected by the SDK host was present + */ + clientManaged?: boolean; + /** + * Whether an actual device MDM/plist/registry/file managed-settings layer was present */ deviceManaged: boolean; /** @@ -8696,7 +8704,7 @@ export interface ManagedSettingsResolvedData { */ managedKeys: string[]; /** - * Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + * Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. */ permissionsAllowIntersected?: boolean; /** @@ -9133,6 +9141,10 @@ export interface SkillsLoadedSkill { * Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field */ argumentHint?: string; + /** + * Canonical slash command name used to invoke the skill, without the leading '/' + */ + commandName?: string; /** * Description of what the skill does */ diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 41e27fc12..a9511540c 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -1809,6 +1809,12 @@ def to_dict(self) -> dict: result["path"] = from_union([from_str, from_none], self.path) return result +# Experimental: this type is part of an experimental API and may change or be removed. +class DisableBypassPermissionsMode(Enum): + """When set to `disable`, prevents bypass/allow-all permission modes.""" + + DISABLE = "disable" + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtensionPlugin: @@ -2096,6 +2102,8 @@ def to_dict(self) -> dict: class ExtensionSource(Enum): """Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state//extensions/) + + Discovery source for the extension entrypoint. """ PLUGIN = "plugin" PROJECT = "project" @@ -2114,6 +2122,39 @@ class ExtensionStatus(Enum): class ExtensionContextPushInputType(Enum): EXTENSION_CONTEXT = "extension_context" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProfile: + """Opaque integrator-owned process launch profile for one extension entrypoint. + + Opaque launch profile, omitted when this provider does not support the entrypoint. + """ + args: list[str] + """Opaque integrator-defined arguments passed to the executable. The runtime does not append + the extension entrypoint. + """ + env: dict[str, str] + """Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, + SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + """ + executable: str + """Executable used to launch the extension entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProfile': + assert isinstance(obj, dict) + args = from_list(from_str, obj.get("args")) + env = from_dict(from_str, obj.get("env")) + executable = from_str(obj.get("executable")) + return ExtensionLaunchProfile(args, env, executable) + + def to_dict(self) -> dict: + result: dict = {} + result["args"] = from_list(from_str, self.args) + result["env"] = from_dict(from_str, self.env) + result["executable"] = from_str(self.executable) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExtensionsDisableRequest: @@ -3808,6 +3849,34 @@ def to_dict(self) -> dict: result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ManagedSettingsReadResult: + """Validated device-managed settings discovered before a session exists.""" + + error_message: str | None = None + """Discovery or validation error text when managed settings could not be read safely.""" + + settings_json: Any = None + """Validated, canonical managed-settings JSON. Omitted when no managed settings were + discovered or when discovered settings failed validation. + """ + + @staticmethod + def from_dict(obj: Any) -> 'ManagedSettingsReadResult': + assert isinstance(obj, dict) + error_message = from_union([from_str, from_none], obj.get("errorMessage")) + settings_json = obj.get("settingsJson") + return ManagedSettingsReadResult(error_message, settings_json) + + def to_dict(self) -> dict: + result: dict = {} + if self.error_message is not None: + result["errorMessage"] = from_union([from_str, from_none], self.error_message) + if self.settings_json is not None: + result["settingsJson"] = self.settings_json + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class MarketplaceAddResult: @@ -4944,6 +5013,57 @@ def to_dict(self) -> dict: result["enabled"] = from_bool(self.enabled) return result +@dataclass +class Categories: + """The six normalized `/context` header buckets, computed from the same tokenization as + `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + describe window capacity rather than occupied context, so the values do not sum to + `totalTokens`. + """ + buffer: int + """Output reserve plus post-blocking-threshold buffer.""" + + custom_instructions: int + """Custom-instructions tokens (0 when none are configured).""" + + free_space: int + """Remaining unused window capacity (clamped at 0).""" + + mcp_tools: int + """MCP tool-definition tokens.""" + + messages: int + """Conversation (user/assistant/tool) message tokens.""" + + system_prompt: int + """System prompt tokens, excluding custom instructions.""" + + system_tools: int + """Non-MCP tool-definition tokens.""" + + @staticmethod + def from_dict(obj: Any) -> 'Categories': + assert isinstance(obj, dict) + buffer = from_int(obj.get("buffer")) + custom_instructions = from_int(obj.get("customInstructions")) + free_space = from_int(obj.get("freeSpace")) + mcp_tools = from_int(obj.get("mcpTools")) + messages = from_int(obj.get("messages")) + system_prompt = from_int(obj.get("systemPrompt")) + system_tools = from_int(obj.get("systemTools")) + return Categories(buffer, custom_instructions, free_space, mcp_tools, messages, system_prompt, system_tools) + + def to_dict(self) -> dict: + result: dict = {} + result["buffer"] = from_int(self.buffer) + result["customInstructions"] = from_int(self.custom_instructions) + result["freeSpace"] = from_int(self.free_space) + result["mcpTools"] = from_int(self.mcp_tools) + result["messages"] = from_int(self.messages) + result["systemPrompt"] = from_int(self.system_prompt) + result["systemTools"] = from_int(self.system_tools) + return result + @dataclass class Compactions: """Successful compaction history for the session.""" @@ -5862,6 +5982,38 @@ class PermissionDecisionApprovedForSessionKind(Enum): class PermissionDecisionCancelledKind(Enum): CANCELLED = "cancelled" +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionOutcome(Enum): + """Disposition of the permission request as observed by the responding client. + + Disposition of a permission request as observed by the responding client. + """ + AUTOPILOT_DENIED = "autopilot_denied" + AUTO_APPROVED = "auto_approved" + PROMPTED_USER = "prompted_user" + +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionSource(Enum): + """Controlled reason or actor responsible for the response. + + Controlled reason or actor responsible for a permission response. + """ + HOST_POLICY = "host_policy" + HUMAN_RESPONSE = "human_response" + JUDGE_RECOMMENDATION = "judge_recommendation" + UNATTENDED_FALLBACK = "unattended_fallback" + +# Experimental: this type is part of an experimental API and may change or be removed. +class PermissionDecisionSurface(Enum): + """Client surface that submitted the response. + + Client surface that submitted a permission response. + """ + COPILOT_APP = "copilot_app" + PROMPT_MODE = "prompt_mode" + SDK = "sdk" + TUI = "tui" + class PermissionDecisionDeniedByContentExclusionPolicyKind(Enum): DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" @@ -5880,30 +6032,6 @@ class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind(Enum) class PermissionDecisionRejectKind(Enum): REJECT = "reject" -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecisionRequest: - """Pending permission request ID and the decision to apply (approve/reject and scope).""" - - request_id: str - """Request ID of the pending permission request""" - - result: PermissionDecision - """The client's response to the pending permission prompt""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionRequest': - assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = _load_PermissionDecision(obj.get("result")) - return PermissionDecisionRequest(request_id, result) - - def to_dict(self) -> dict: - result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = (self.result).to_dict() - return result - class PermissionDecisionUserNotAvailableKind(Enum): USER_NOT_AVAILABLE = "user-not-available" @@ -8604,6 +8732,9 @@ class ServerSkill: """Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field """ + command_name: str | None = None + """Canonical slash command name used to invoke the skill, without the leading '/'""" + path: str | None = None """Absolute path to the skill file""" @@ -8619,9 +8750,10 @@ def from_dict(obj: Any) -> 'ServerSkill': source = SkillSource(obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_str, from_none], obj.get("argumentHint")) + command_name = from_union([from_str, from_none], obj.get("commandName")) path = from_union([from_str, from_none], obj.get("path")) project_path = from_union([from_str, from_none], obj.get("projectPath")) - return ServerSkill(description, enabled, name, source, user_invocable, argument_hint, path, project_path) + return ServerSkill(description, enabled, name, source, user_invocable, argument_hint, command_name, path, project_path) def to_dict(self) -> dict: result: dict = {} @@ -8632,6 +8764,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_str, from_none], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_str, from_none], self.command_name) if self.path is not None: result["path"] = from_union([from_str, from_none], self.path) if self.project_path is not None: @@ -10794,6 +10928,9 @@ class Skill: """Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field """ + command_name: str | None = None + """Canonical slash command name used to invoke the skill, without the leading '/'""" + path: str | None = None """Absolute path to the skill file""" @@ -10809,9 +10946,10 @@ def from_dict(obj: Any) -> 'Skill': source = SkillSource(obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_str, from_none], obj.get("argumentHint")) + command_name = from_union([from_str, from_none], obj.get("commandName")) path = from_union([from_str, from_none], obj.get("path")) plugin_name = from_union([from_str, from_none], obj.get("pluginName")) - return Skill(description, enabled, name, source, user_invocable, argument_hint, path, plugin_name) + return Skill(description, enabled, name, source, user_invocable, argument_hint, command_name, path, plugin_name) def to_dict(self) -> dict: result: dict = {} @@ -10822,6 +10960,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_str, from_none], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_str, from_none], self.command_name) if self.path is not None: result["path"] = from_union([from_str, from_none], self.path) if self.plugin_name is not None: @@ -13192,6 +13332,45 @@ def to_dict(self) -> dict: result["outputDirectory"] = from_union([from_str, from_none], self.output_directory) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionManagedPermissions: + """Enterprise permission policy expressed with the runtime's managed permission-rule syntax.""" + + allow: list[str] | None = None + """Permission rules that allow matching operations unless another managed source, deny, or + ask rule restricts them. + """ + ask: list[str] | None = None + """Permission rules that require explicit human approval.""" + + deny: list[str] | None = None + """Permission rules that block matching operations. Deny has highest precedence.""" + + disable_bypass_permissions_mode: DisableBypassPermissionsMode | None = None + """When set to `disable`, prevents bypass/allow-all permission modes.""" + + @staticmethod + def from_dict(obj: Any) -> 'SessionManagedPermissions': + assert isinstance(obj, dict) + allow = from_union([lambda x: from_list(from_str, x), from_none], obj.get("allow")) + ask = from_union([lambda x: from_list(from_str, x), from_none], obj.get("ask")) + deny = from_union([lambda x: from_list(from_str, x), from_none], obj.get("deny")) + disable_bypass_permissions_mode = from_union([DisableBypassPermissionsMode, from_none], obj.get("disableBypassPermissionsMode")) + return SessionManagedPermissions(allow, ask, deny, disable_bypass_permissions_mode) + + def to_dict(self) -> dict: + result: dict = {} + if self.allow is not None: + result["allow"] = from_union([lambda x: from_list(from_str, x), from_none], self.allow) + if self.ask is not None: + result["ask"] = from_union([lambda x: from_list(from_str, x), from_none], self.ask) + if self.deny is not None: + result["deny"] = from_union([lambda x: from_list(from_str, x), from_none], self.deny) + if self.disable_bypass_permissions_mode is not None: + result["disableBypassPermissionsMode"] = from_union([lambda x: to_enum(DisableBypassPermissionsMode, x), from_none], self.disable_bypass_permissions_mode) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtension: @@ -13382,6 +13561,41 @@ def to_dict(self) -> dict: result["hasMore"] = from_bool(self.has_more) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProviderResolveRequest: + """A discovered extension entrypoint that the registered integrator may classify and resolve + to an opaque launch profile. + """ + id: str + """Source-qualified extension identifier.""" + + module_path: str + """Absolute path to the discovered extension entrypoint.""" + + name: str + """Human-readable extension name.""" + + source: ExtensionSource + """Discovery source for the extension entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProviderResolveRequest': + assert isinstance(obj, dict) + id = from_str(obj.get("id")) + module_path = from_str(obj.get("modulePath")) + name = from_str(obj.get("name")) + source = ExtensionSource(obj.get("source")) + return ExtensionLaunchProviderResolveRequest(id, module_path, name, source) + + def to_dict(self) -> dict: + result: dict = {} + result["id"] = from_str(self.id) + result["modulePath"] = from_str(self.module_path) + result["name"] = from_str(self.name) + result["source"] = to_enum(ExtensionSource, self.source) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class Extension: @@ -13453,6 +13667,27 @@ def to_dict(self) -> dict: result["type"] = self.type return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class ExtensionLaunchProviderResolveResult: + """The launch profile for a supported entrypoint. Omit launch when the provider does not + support the entrypoint. + """ + launch: ExtensionLaunchProfile | None = None + """Opaque launch profile, omitted when this provider does not support the entrypoint.""" + + @staticmethod + def from_dict(obj: Any) -> 'ExtensionLaunchProviderResolveResult': + assert isinstance(obj, dict) + launch = from_union([ExtensionLaunchProfile.from_dict, from_none], obj.get("launch")) + return ExtensionLaunchProviderResolveResult(launch) + + def to_dict(self) -> dict: + result: dict = {} + if self.launch is not None: + result["launch"] = from_union([lambda x: to_class(ExtensionLaunchProfile, x), from_none], self.launch) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class ExternalToolTextResultForLlmBinaryResultsForLlm: @@ -15869,13 +16104,47 @@ class SessionContextAttribution: """Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. """ + buffer_tokens: int + """Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors + `SessionContextInfo.bufferTokens`. + """ + categories: Categories + """The six normalized `/context` header buckets, computed from the same tokenization as + `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` + describe window capacity rather than occupied context, so the values do not sum to + `totalTokens`. + """ compactions: Compactions """Successful compaction history for the session.""" + compaction_threshold: int + """Token count at which background compaction starts. Mirrors + `SessionContextInfo.compactionThreshold`. + """ entries: list[Entry] """Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. """ + limit: int + """Prompt limit plus the model's output reserve: the full context window + `categories.freeSpace` and `categories.buffer` are measured against. Mirrors + `SessionContextInfo.limit`. + """ + model_id: str + """The concrete model id the entire breakdown was tokenized against (feeds the per-model + token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the + literal `auto` sentinel, so totals are not undercounted. A single-model approximation of + a potentially multi-model Auto session. + """ + model_source: str + """How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: + `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected + model), `default` (a fallback before any model is known). + """ + prompt_token_limit: int + """Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` + context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + """ total_tokens: int """Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by @@ -15885,15 +16154,29 @@ class SessionContextAttribution: @staticmethod def from_dict(obj: Any) -> 'SessionContextAttribution': assert isinstance(obj, dict) + buffer_tokens = from_int(obj.get("bufferTokens")) + categories = Categories.from_dict(obj.get("categories")) compactions = Compactions.from_dict(obj.get("compactions")) + compaction_threshold = from_int(obj.get("compactionThreshold")) entries = from_list(Entry.from_dict, obj.get("entries")) + limit = from_int(obj.get("limit")) + model_id = from_str(obj.get("modelId")) + model_source = from_str(obj.get("modelSource")) + prompt_token_limit = from_int(obj.get("promptTokenLimit")) total_tokens = from_int(obj.get("totalTokens")) - return SessionContextAttribution(compactions, entries, total_tokens) + return SessionContextAttribution(buffer_tokens, categories, compactions, compaction_threshold, entries, limit, model_id, model_source, prompt_token_limit, total_tokens) def to_dict(self) -> dict: result: dict = {} + result["bufferTokens"] = from_int(self.buffer_tokens) + result["categories"] = to_class(Categories, self.categories) result["compactions"] = to_class(Compactions, self.compactions) + result["compactionThreshold"] = from_int(self.compaction_threshold) result["entries"] = from_list(lambda x: to_class(Entry, x), self.entries) + result["limit"] = from_int(self.limit) + result["modelId"] = from_str(self.model_id) + result["modelSource"] = from_str(self.model_source) + result["promptTokenLimit"] = from_int(self.prompt_token_limit) result["totalTokens"] = from_int(self.total_tokens) return result @@ -17218,6 +17501,39 @@ def to_dict(self) -> dict: result["reason"] = from_union([from_str, from_none], self.reason) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionContext: + """Optional informational context describing how and where the permission decision was made. + This does not affect permission behavior. + + Optional informational context describing how and where this response was made. Omit it + to preserve legacy behavior without attributing an origin. + """ + outcome: PermissionDecisionOutcome + """Disposition of the permission request as observed by the responding client.""" + + source: PermissionDecisionSource + """Controlled reason or actor responsible for the response.""" + + surface: PermissionDecisionSurface + """Client surface that submitted the response.""" + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionContext': + assert isinstance(obj, dict) + outcome = PermissionDecisionOutcome(obj.get("outcome")) + source = PermissionDecisionSource(obj.get("source")) + surface = PermissionDecisionSurface(obj.get("surface")) + return PermissionDecisionContext(outcome, source, surface) + + def to_dict(self) -> dict: + result: dict = {} + result["outcome"] = to_enum(PermissionDecisionOutcome, self.outcome) + result["source"] = to_enum(PermissionDecisionSource, self.source) + result["surface"] = to_enum(PermissionDecisionSurface, self.surface) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionDecisionDeniedByContentExclusionPolicy: @@ -21430,6 +21746,29 @@ def to_dict(self) -> dict: result["skippedEntries"] = from_union([lambda x: from_list(lambda x: to_class(DebugCollectLogsSkippedEntry, x), x), from_none], self.skipped_entries) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionManagedSettings: + """Managed settings an SDK host may inject at session startup. Only permissions are accepted + in this initial contract. + + Permissions-only enterprise policy injected by the SDK host at session create or resume. + Composes restrictively with self-fetched and device policy and is not persisted. + """ + permissions: SessionManagedPermissions | None = None + + @staticmethod + def from_dict(obj: Any) -> 'SessionManagedSettings': + assert isinstance(obj, dict) + permissions = from_union([SessionManagedPermissions.from_dict, from_none], obj.get("permissions")) + return SessionManagedSettings(permissions) + + def to_dict(self) -> dict: + result: dict = {} + if self.permissions is not None: + result["permissions"] = from_union([lambda x: to_class(SessionManagedPermissions, x), from_none], self.permissions) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class DiscoveredExtensions: @@ -23030,6 +23369,38 @@ def to_dict(self) -> dict: result["scope"] = to_enum(AdditionalContentExclusionPolicyScope, self.scope) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionRequest: + """Pending permission request ID and the decision to apply (approve/reject and scope).""" + + request_id: str + """Request ID of the pending permission request""" + + result: PermissionDecision + """The client's response to the pending permission prompt""" + + decision_context: PermissionDecisionContext | None = None + """Optional informational context describing how and where this response was made. Omit it + to preserve legacy behavior without attributing an origin. + """ + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = _load_PermissionDecision(obj.get("result")) + decision_context = from_union([PermissionDecisionContext.from_dict, from_none], obj.get("decisionContext")) + return PermissionDecisionRequest(request_id, result, decision_context) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = (self.result).to_dict() + if self.decision_context is not None: + result["decisionContext"] = from_union([lambda x: to_class(PermissionDecisionContext, x), from_none], self.decision_context) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class PermissionsConfigureAdditionalContentExclusionPolicy: @@ -25267,13 +25638,14 @@ class SandboxConfig: add_current_working_directory: bool | None = None """Whether to auto-add the current working directory to readwritePaths. Default: true.""" - allow_dev_tool_caches: bool | None = None + allow_dev_tool_access: bool | None = None """Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so - builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; - set to false to opt out). + builds work without extra configuration; a relocated CARGO_HOME additionally gets its + Cargo lock files granted read-write. Default: true (enabled by default; set to false to + opt out). """ gh_auth: bool | None = None """Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the @@ -25292,19 +25664,19 @@ def from_dict(obj: Any) -> 'SandboxConfig': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) add_current_working_directory = from_union([from_bool, from_none], obj.get("addCurrentWorkingDirectory")) - allow_dev_tool_caches = from_union([from_bool, from_none], obj.get("allowDevToolCaches")) + allow_dev_tool_access = from_union([from_bool, from_none], obj.get("allowDevToolAccess")) gh_auth = from_union([from_bool, from_none], obj.get("ghAuth")) git_auth = from_union([from_bool, from_none], obj.get("gitAuth")) user_policy = from_union([SandboxConfigUserPolicy.from_dict, from_none], obj.get("userPolicy")) - return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_caches, gh_auth, git_auth, user_policy) + return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_access, gh_auth, git_auth, user_policy) def to_dict(self) -> dict: result: dict = {} result["enabled"] = from_bool(self.enabled) if self.add_current_working_directory is not None: result["addCurrentWorkingDirectory"] = from_union([from_bool, from_none], self.add_current_working_directory) - if self.allow_dev_tool_caches is not None: - result["allowDevToolCaches"] = from_union([from_bool, from_none], self.allow_dev_tool_caches) + if self.allow_dev_tool_access is not None: + result["allowDevToolAccess"] = from_union([from_bool, from_none], self.allow_dev_tool_access) if self.gh_auth is not None: result["ghAuth"] = from_union([from_bool, from_none], self.gh_auth) if self.git_auth is not None: @@ -25529,6 +25901,10 @@ class SessionOpenOptions: disabled_instruction_sources: list[str] | None = None """Instruction source IDs disabled for this session.""" + disabled_mcp_servers: list[str] | None = None + """MCP server names disabled for this session. Disabled servers are not started or + authenticated on create or cold resume. + """ disabled_skills: list[str] | None = None """Skill IDs disabled for this session.""" @@ -25618,6 +25994,10 @@ class SessionOpenOptions: lsp_client_name: str | None = None """Identifier sent to LSP-style integrations.""" + managed_settings: SessionManagedSettings | None = None + """Permissions-only enterprise policy injected by the SDK host at session create or resume. + Composes restrictively with self-fetched and device policy and is not persisted. + """ max_inline_binary_bytes: int | None = None """Maximum decoded byte size of a single inline model-facing binary tool result persisted in session events (default 10 MB). @@ -25725,6 +26105,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': detached_from_spawning_parent_engagement_id = from_union([from_str, from_none], obj.get("detachedFromSpawningParentEngagementId")) detached_from_spawning_parent_session_id = from_union([from_str, from_none], obj.get("detachedFromSpawningParentSessionId")) disabled_instruction_sources = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledInstructionSources")) + disabled_mcp_servers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledMcpServers")) disabled_skills = from_union([lambda x: from_list(from_str, x), from_none], obj.get("disabledSkills")) enable_citations = from_union([from_bool, from_none], obj.get("enableCitations")) enable_file_change_tracking = from_union([from_bool, from_none], obj.get("enableFileChangeTracking")) @@ -25745,6 +26126,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': is_experimental_mode = from_union([from_bool, from_none], obj.get("isExperimentalMode")) log_interactive_shells = from_union([from_bool, from_none], obj.get("logInteractiveShells")) lsp_client_name = from_union([from_str, from_none], obj.get("lspClientName")) + managed_settings = from_union([SessionManagedSettings.from_dict, from_none], obj.get("managedSettings")) max_inline_binary_bytes = from_union([from_int, from_none], obj.get("maxInlineBinaryBytes")) memory = from_union([MemoryConfiguration.from_dict, from_none], obj.get("memory")) model = from_union([from_str, from_none], obj.get("model")) @@ -25772,7 +26154,7 @@ def from_dict(obj: Any) -> 'SessionOpenOptions': verbosity = from_union([Verbosity, from_none], obj.get("verbosity")) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) working_directory_context = from_union([SessionContext.from_dict, from_none], obj.get("workingDirectoryContext")) - return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, auth_info, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, included_builtin_agents, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) + return SessionOpenOptions(additional_content_exclusion_policies, additional_directories, agent_context, allow_all_mcp_server_instructions, ask_user_disabled, auth_info, available_tools, capi, client_kind, client_name, coauthor_enabled, config_dir, continue_on_auto_mode, copilot_url, custom_agents_local_only, detached_from_spawning_parent_engagement_id, detached_from_spawning_parent_session_id, disabled_instruction_sources, disabled_mcp_servers, disabled_skills, enable_citations, enable_file_change_tracking, enable_managed_settings, enable_on_demand_instruction_discovery, enable_script_safety, enable_streaming, env_value_mode, events_log_directory, events_log_includes_subagents, excluded_builtin_agents, excluded_tools, exp_assignments, feature_flags, included_builtin_agents, installed_plugins, integration_id, is_experimental_mode, log_interactive_shells, lsp_client_name, managed_settings, max_inline_binary_bytes, memory, model, model_capabilities_overrides, models, name, provider, providers, reasoning_effort, reasoning_summary, remote_defaulted_on, remote_exporting, remote_steerable, running_in_interactive_mode, sandbox_config, session_capabilities, session_id, session_limits, shell, shell_init_profile, shell_process_flags, skill_directories, skip_custom_instructions, trajectory_file, verbosity, working_directory, working_directory_context) def to_dict(self) -> dict: result: dict = {} @@ -25812,6 +26194,8 @@ def to_dict(self) -> dict: result["detachedFromSpawningParentSessionId"] = from_union([from_str, from_none], self.detached_from_spawning_parent_session_id) if self.disabled_instruction_sources is not None: result["disabledInstructionSources"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_instruction_sources) + if self.disabled_mcp_servers is not None: + result["disabledMcpServers"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_mcp_servers) if self.disabled_skills is not None: result["disabledSkills"] = from_union([lambda x: from_list(from_str, x), from_none], self.disabled_skills) if self.enable_citations is not None: @@ -25852,6 +26236,8 @@ def to_dict(self) -> dict: result["logInteractiveShells"] = from_union([from_bool, from_none], self.log_interactive_shells) if self.lsp_client_name is not None: result["lspClientName"] = from_union([from_str, from_none], self.lsp_client_name) + if self.managed_settings is not None: + result["managedSettings"] = from_union([lambda x: to_class(SessionManagedSettings, x), from_none], self.managed_settings) if self.max_inline_binary_bytes is not None: result["maxInlineBinaryBytes"] = from_union([from_int, from_none], self.max_inline_binary_bytes) if self.memory is not None: @@ -28448,6 +28834,7 @@ class RPC: debug_collect_logs_result_kind: DebugCollectLogsResultKind debug_collect_logs_skipped_entry: DebugCollectLogsSkippedEntry debug_collect_logs_source: DebugCollectLogsSource + disable_bypass_permissions_mode: DisableBypassPermissionsMode discovered_canvas: DiscoveredCanvas discovered_extension: DiscoveredExtension discovered_extension_mode: DiscoveredExtensionMode @@ -28473,6 +28860,9 @@ class RPC: execute_command_result: ExecuteCommandResult extension: Extension extension_context_push_input: ExtensionContextPushInput + extension_launch_profile: ExtensionLaunchProfile + extension_launch_provider_resolve_request: ExtensionLaunchProviderResolveRequest + extension_launch_provider_resolve_result: ExtensionLaunchProviderResolveResult extension_list: ExtensionList extensions_disable_request: ExtensionsDisableRequest extensions_enable_request: ExtensionsEnableRequest @@ -28604,6 +28994,7 @@ class RPC: log_request: LogRequest log_result: LogResult lsp_initialize_request: LspInitializeRequest + managed_settings_read_result: ManagedSettingsReadResult marketplace_add_result: MarketplaceAddResult marketplace_browse_result: MarketplaceBrowseResult marketplace_info: MarketplaceInfo @@ -28798,13 +29189,17 @@ class RPC: permission_decision_approve_once: PermissionDecisionApproveOnce permission_decision_approve_permanently: PermissionDecisionApprovePermanently permission_decision_cancelled: PermissionDecisionCancelled + permission_decision_context: PermissionDecisionContext permission_decision_denied_by_content_exclusion_policy: PermissionDecisionDeniedByContentExclusionPolicy permission_decision_denied_by_permission_request_hook: PermissionDecisionDeniedByPermissionRequestHook permission_decision_denied_by_rules: PermissionDecisionDeniedByRules permission_decision_denied_interactively_by_user: PermissionDecisionDeniedInteractivelyByUser permission_decision_denied_no_approval_rule_and_could_not_request_from_user: PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser + permission_decision_outcome: PermissionDecisionOutcome permission_decision_reject: PermissionDecisionReject permission_decision_request: PermissionDecisionRequest + permission_decision_source: PermissionDecisionSource + permission_decision_surface: PermissionDecisionSurface permission_decision_user_not_available: PermissionDecisionUserNotAvailable permission_location_add_tool_approval_params: PermissionLocationAddToolApprovalParams permission_location_apply_params: PermissionLocationApplyParams @@ -29083,6 +29478,8 @@ class RPC: session_list_filter: SessionListFilter session_load_deferred_repo_hooks_result: SessionLoadDeferredRepoHooksResult session_log_level: SessionLogLevel + session_managed_permissions: SessionManagedPermissions + session_managed_settings: SessionManagedSettings session_mcp_apps_call_tool_result: dict[str, Any] session_metadata_snapshot: SessionMetadataSnapshot session_mode: SessionMode @@ -29449,6 +29846,7 @@ def from_dict(obj: Any) -> 'RPC': debug_collect_logs_result_kind = DebugCollectLogsResultKind(obj.get("DebugCollectLogsResultKind")) debug_collect_logs_skipped_entry = DebugCollectLogsSkippedEntry.from_dict(obj.get("DebugCollectLogsSkippedEntry")) debug_collect_logs_source = DebugCollectLogsSource(obj.get("DebugCollectLogsSource")) + disable_bypass_permissions_mode = DisableBypassPermissionsMode(obj.get("DisableBypassPermissionsMode")) discovered_canvas = DiscoveredCanvas.from_dict(obj.get("DiscoveredCanvas")) discovered_extension = DiscoveredExtension.from_dict(obj.get("DiscoveredExtension")) discovered_extension_mode = DiscoveredExtensionMode(obj.get("DiscoveredExtensionMode")) @@ -29474,6 +29872,9 @@ def from_dict(obj: Any) -> 'RPC': execute_command_result = ExecuteCommandResult.from_dict(obj.get("ExecuteCommandResult")) extension = Extension.from_dict(obj.get("Extension")) extension_context_push_input = ExtensionContextPushInput.from_dict(obj.get("ExtensionContextPushInput")) + extension_launch_profile = ExtensionLaunchProfile.from_dict(obj.get("ExtensionLaunchProfile")) + extension_launch_provider_resolve_request = ExtensionLaunchProviderResolveRequest.from_dict(obj.get("ExtensionLaunchProviderResolveRequest")) + extension_launch_provider_resolve_result = ExtensionLaunchProviderResolveResult.from_dict(obj.get("ExtensionLaunchProviderResolveResult")) extension_list = ExtensionList.from_dict(obj.get("ExtensionList")) extensions_disable_request = ExtensionsDisableRequest.from_dict(obj.get("ExtensionsDisableRequest")) extensions_enable_request = ExtensionsEnableRequest.from_dict(obj.get("ExtensionsEnableRequest")) @@ -29605,6 +30006,7 @@ def from_dict(obj: Any) -> 'RPC': log_request = LogRequest.from_dict(obj.get("LogRequest")) log_result = LogResult.from_dict(obj.get("LogResult")) lsp_initialize_request = LspInitializeRequest.from_dict(obj.get("LspInitializeRequest")) + managed_settings_read_result = ManagedSettingsReadResult.from_dict(obj.get("ManagedSettingsReadResult")) marketplace_add_result = MarketplaceAddResult.from_dict(obj.get("MarketplaceAddResult")) marketplace_browse_result = MarketplaceBrowseResult.from_dict(obj.get("MarketplaceBrowseResult")) marketplace_info = MarketplaceInfo.from_dict(obj.get("MarketplaceInfo")) @@ -29799,13 +30201,17 @@ def from_dict(obj: Any) -> 'RPC': permission_decision_approve_once = PermissionDecisionApproveOnce.from_dict(obj.get("PermissionDecisionApproveOnce")) permission_decision_approve_permanently = PermissionDecisionApprovePermanently.from_dict(obj.get("PermissionDecisionApprovePermanently")) permission_decision_cancelled = PermissionDecisionCancelled.from_dict(obj.get("PermissionDecisionCancelled")) + permission_decision_context = PermissionDecisionContext.from_dict(obj.get("PermissionDecisionContext")) permission_decision_denied_by_content_exclusion_policy = PermissionDecisionDeniedByContentExclusionPolicy.from_dict(obj.get("PermissionDecisionDeniedByContentExclusionPolicy")) permission_decision_denied_by_permission_request_hook = PermissionDecisionDeniedByPermissionRequestHook.from_dict(obj.get("PermissionDecisionDeniedByPermissionRequestHook")) permission_decision_denied_by_rules = PermissionDecisionDeniedByRules.from_dict(obj.get("PermissionDecisionDeniedByRules")) permission_decision_denied_interactively_by_user = PermissionDecisionDeniedInteractivelyByUser.from_dict(obj.get("PermissionDecisionDeniedInteractivelyByUser")) permission_decision_denied_no_approval_rule_and_could_not_request_from_user = PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser.from_dict(obj.get("PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser")) + permission_decision_outcome = PermissionDecisionOutcome(obj.get("PermissionDecisionOutcome")) permission_decision_reject = PermissionDecisionReject.from_dict(obj.get("PermissionDecisionReject")) permission_decision_request = PermissionDecisionRequest.from_dict(obj.get("PermissionDecisionRequest")) + permission_decision_source = PermissionDecisionSource(obj.get("PermissionDecisionSource")) + permission_decision_surface = PermissionDecisionSurface(obj.get("PermissionDecisionSurface")) permission_decision_user_not_available = PermissionDecisionUserNotAvailable.from_dict(obj.get("PermissionDecisionUserNotAvailable")) permission_location_add_tool_approval_params = PermissionLocationAddToolApprovalParams.from_dict(obj.get("PermissionLocationAddToolApprovalParams")) permission_location_apply_params = PermissionLocationApplyParams.from_dict(obj.get("PermissionLocationApplyParams")) @@ -30084,6 +30490,8 @@ def from_dict(obj: Any) -> 'RPC': session_list_filter = SessionListFilter.from_dict(obj.get("SessionListFilter")) session_load_deferred_repo_hooks_result = SessionLoadDeferredRepoHooksResult.from_dict(obj.get("SessionLoadDeferredRepoHooksResult")) session_log_level = SessionLogLevel(obj.get("SessionLogLevel")) + session_managed_permissions = SessionManagedPermissions.from_dict(obj.get("SessionManagedPermissions")) + session_managed_settings = SessionManagedSettings.from_dict(obj.get("SessionManagedSettings")) session_mcp_apps_call_tool_result = from_dict(lambda x: x, obj.get("SessionMcpAppsCallToolResult")) session_metadata_snapshot = SessionMetadataSnapshot.from_dict(obj.get("SessionMetadataSnapshot")) session_mode = SessionMode(obj.get("SessionMode")) @@ -30337,7 +30745,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, disable_bypass_permissions_mode, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, managed_settings_read_result, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_context, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_outcome, permission_decision_reject, permission_decision_request, permission_decision_source, permission_decision_surface, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -30450,6 +30858,7 @@ def to_dict(self) -> dict: result["DebugCollectLogsResultKind"] = to_enum(DebugCollectLogsResultKind, self.debug_collect_logs_result_kind) result["DebugCollectLogsSkippedEntry"] = to_class(DebugCollectLogsSkippedEntry, self.debug_collect_logs_skipped_entry) result["DebugCollectLogsSource"] = to_enum(DebugCollectLogsSource, self.debug_collect_logs_source) + result["DisableBypassPermissionsMode"] = to_enum(DisableBypassPermissionsMode, self.disable_bypass_permissions_mode) result["DiscoveredCanvas"] = to_class(DiscoveredCanvas, self.discovered_canvas) result["DiscoveredExtension"] = to_class(DiscoveredExtension, self.discovered_extension) result["DiscoveredExtensionMode"] = to_enum(DiscoveredExtensionMode, self.discovered_extension_mode) @@ -30475,6 +30884,9 @@ def to_dict(self) -> dict: result["ExecuteCommandResult"] = to_class(ExecuteCommandResult, self.execute_command_result) result["Extension"] = to_class(Extension, self.extension) result["ExtensionContextPushInput"] = to_class(ExtensionContextPushInput, self.extension_context_push_input) + result["ExtensionLaunchProfile"] = to_class(ExtensionLaunchProfile, self.extension_launch_profile) + result["ExtensionLaunchProviderResolveRequest"] = to_class(ExtensionLaunchProviderResolveRequest, self.extension_launch_provider_resolve_request) + result["ExtensionLaunchProviderResolveResult"] = to_class(ExtensionLaunchProviderResolveResult, self.extension_launch_provider_resolve_result) result["ExtensionList"] = to_class(ExtensionList, self.extension_list) result["ExtensionsDisableRequest"] = to_class(ExtensionsDisableRequest, self.extensions_disable_request) result["ExtensionsEnableRequest"] = to_class(ExtensionsEnableRequest, self.extensions_enable_request) @@ -30606,6 +31018,7 @@ def to_dict(self) -> dict: result["LogRequest"] = to_class(LogRequest, self.log_request) result["LogResult"] = to_class(LogResult, self.log_result) result["LspInitializeRequest"] = to_class(LspInitializeRequest, self.lsp_initialize_request) + result["ManagedSettingsReadResult"] = to_class(ManagedSettingsReadResult, self.managed_settings_read_result) result["MarketplaceAddResult"] = to_class(MarketplaceAddResult, self.marketplace_add_result) result["MarketplaceBrowseResult"] = to_class(MarketplaceBrowseResult, self.marketplace_browse_result) result["MarketplaceInfo"] = to_class(MarketplaceInfo, self.marketplace_info) @@ -30800,13 +31213,17 @@ def to_dict(self) -> dict: result["PermissionDecisionApproveOnce"] = to_class(PermissionDecisionApproveOnce, self.permission_decision_approve_once) result["PermissionDecisionApprovePermanently"] = to_class(PermissionDecisionApprovePermanently, self.permission_decision_approve_permanently) result["PermissionDecisionCancelled"] = to_class(PermissionDecisionCancelled, self.permission_decision_cancelled) + result["PermissionDecisionContext"] = to_class(PermissionDecisionContext, self.permission_decision_context) result["PermissionDecisionDeniedByContentExclusionPolicy"] = to_class(PermissionDecisionDeniedByContentExclusionPolicy, self.permission_decision_denied_by_content_exclusion_policy) result["PermissionDecisionDeniedByPermissionRequestHook"] = to_class(PermissionDecisionDeniedByPermissionRequestHook, self.permission_decision_denied_by_permission_request_hook) result["PermissionDecisionDeniedByRules"] = to_class(PermissionDecisionDeniedByRules, self.permission_decision_denied_by_rules) result["PermissionDecisionDeniedInteractivelyByUser"] = to_class(PermissionDecisionDeniedInteractivelyByUser, self.permission_decision_denied_interactively_by_user) result["PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser"] = to_class(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser, self.permission_decision_denied_no_approval_rule_and_could_not_request_from_user) + result["PermissionDecisionOutcome"] = to_enum(PermissionDecisionOutcome, self.permission_decision_outcome) result["PermissionDecisionReject"] = to_class(PermissionDecisionReject, self.permission_decision_reject) result["PermissionDecisionRequest"] = to_class(PermissionDecisionRequest, self.permission_decision_request) + result["PermissionDecisionSource"] = to_enum(PermissionDecisionSource, self.permission_decision_source) + result["PermissionDecisionSurface"] = to_enum(PermissionDecisionSurface, self.permission_decision_surface) result["PermissionDecisionUserNotAvailable"] = to_class(PermissionDecisionUserNotAvailable, self.permission_decision_user_not_available) result["PermissionLocationAddToolApprovalParams"] = to_class(PermissionLocationAddToolApprovalParams, self.permission_location_add_tool_approval_params) result["PermissionLocationApplyParams"] = to_class(PermissionLocationApplyParams, self.permission_location_apply_params) @@ -31085,6 +31502,8 @@ def to_dict(self) -> dict: result["SessionListFilter"] = to_class(SessionListFilter, self.session_list_filter) result["SessionLoadDeferredRepoHooksResult"] = to_class(SessionLoadDeferredRepoHooksResult, self.session_load_deferred_repo_hooks_result) result["SessionLogLevel"] = to_enum(SessionLogLevel, self.session_log_level) + result["SessionManagedPermissions"] = to_class(SessionManagedPermissions, self.session_managed_permissions) + result["SessionManagedSettings"] = to_class(SessionManagedSettings, self.session_managed_settings) result["SessionMcpAppsCallToolResult"] = from_dict(lambda x: x, self.session_mcp_apps_call_tool_result) result["SessionMetadataSnapshot"] = to_class(SessionMetadataSnapshot, self.session_metadata_snapshot) result["SessionMode"] = to_enum(SessionMode, self.session_mode) @@ -31960,6 +32379,16 @@ def __init__(self, client: "JsonRpcClient"): self.settings = ServerUserSettingsApi(client) +# Experimental: this API group is experimental and may change or be removed. +class ServerManagedSettingsApi: + def __init__(self, client: "JsonRpcClient"): + self._client = client + + async def read(self, *, timeout: float | None = None) -> ManagedSettingsReadResult: + "Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session.\n\nReturns:\n Validated device-managed settings discovered before a session exists." + return ManagedSettingsReadResult.from_dict(await self._client.request("managedSettings.read", {}, **_timeout_kwargs(timeout))) + + # Experimental: this API group is experimental and may change or be removed. class ServerRuntimeApi: def __init__(self, client: "JsonRpcClient"): @@ -32147,6 +32576,7 @@ def __init__(self, client: "JsonRpcClient"): self.instructions = ServerInstructionsApi(client) self.commands = ServerCommandsApi(client) self.user = ServerUserApi(client) + self.managed_settings = ServerManagedSettingsApi(client) self.runtime = ServerRuntimeApi(client) self.session_fs = ServerSessionFsApi(client) self.llm_inference = ServerLlmInferenceApi(client) @@ -32158,6 +32588,10 @@ async def ping(self, params: PingRequest, *, timeout: float | None = None) -> Pi params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return PingResult.from_dict(await self._client.request("ping", params_dict, **_timeout_kwargs(timeout))) + async def register_extension_launch_provider(self, *, timeout: float | None = None) -> None: + "Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility.\n\n.. warning:: This API is experimental and may change or be removed in future versions." + await self._client.request("registerExtensionLaunchProvider", {}, **_timeout_kwargs(timeout)) + # Experimental: this API group is experimental and may change or be removed. class _InternalServerSessionsApi: @@ -34118,6 +34552,12 @@ async def invoke(self, params: _HookInvokeRequest) -> _HookInvokeResponse: "Dispatches one SDK callback hook from the runtime to the connection that registered it. Internal transport plumbing: clients opt in through session initialization and the Rust hook processor owns ordering, policy, timeout, and callback routing.\n\nArgs:\n params: Runtime-owned wire payload for a server-to-client hook callback invocation.\n\nReturns:\n Optional output returned by an SDK callback hook." pass +# Experimental: this API group is experimental and may change or be removed. +class ExtensionLaunchProviderHandler(Protocol): + async def resolve(self, params: ExtensionLaunchProviderResolveRequest) -> ExtensionLaunchProviderResolveResult: + "Asks the registered SDK client to resolve an opaque process launch profile for one discovered extension entrypoint immediately before launch or reload. The provider must respond within 15 seconds.\n\nArgs:\n params: A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile.\n\nReturns:\n The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint." + pass + # Experimental: this API group is experimental and may change or be removed. class LlmInferenceHandler(Protocol): async def http_request_start(self, params: LlmInferenceHTTPRequestStartRequest) -> LlmInferenceHTTPRequestStartResult: @@ -34136,6 +34576,7 @@ async def event(self, params: GitHubTelemetryNotification) -> None: @dataclass class ClientGlobalApiHandlers: hooks: HooksHandler | None = None + extension_launch_provider: ExtensionLaunchProviderHandler | None = None llm_inference: LlmInferenceHandler | None = None git_hub_telemetry: GitHubTelemetryHandler | None = None @@ -34156,6 +34597,13 @@ async def handle_hooks_invoke(params: dict) -> dict | None: result = await handler.invoke(request) return result.to_dict() client.set_request_handler("hooks.invoke", handle_hooks_invoke) + async def handle_extension_launch_provider_resolve(params: dict) -> dict | None: + request = ExtensionLaunchProviderResolveRequest.from_dict(params) + handler = handlers.extension_launch_provider + if handler is None: raise RuntimeError("No extension_launch_provider client-global handler registered") + result = await handler.resolve(request) + return result.to_dict() + client.set_request_handler("extensionLaunchProvider.resolve", handle_extension_launch_provider_resolve) async def handle_llm_inference_http_request_start(params: dict) -> dict | None: request = LlmInferenceHTTPRequestStartRequest.from_dict(params) handler = handlers.llm_inference @@ -34258,6 +34706,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "CanvasProviderOpenResult", "CanvasSessionContext", "CapiSessionOptions", + "Categories", "ClientGlobalApiHandlers", "ClientSessionApiHandlers", "CommandList", @@ -34305,6 +34754,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "DebugCollectLogsResultKind", "DebugCollectLogsSkippedEntry", "DebugCollectLogsSource", + "DisableBypassPermissionsMode", "DiscoveredCanvas", "DiscoveredExtension", "DiscoveredExtensionMode", @@ -34334,6 +34784,10 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "Extension", "ExtensionContextPushInput", "ExtensionContextPushInputType", + "ExtensionLaunchProfile", + "ExtensionLaunchProviderHandler", + "ExtensionLaunchProviderResolveRequest", + "ExtensionLaunchProviderResolveResult", "ExtensionList", "ExtensionSource", "ExtensionStatus", @@ -34579,6 +35033,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "MCPToolUIVisibility", "MCPTools", "MCPUnregisterExternalClientRequest", + "ManagedSettingsReadResult", "MarketplaceAddResult", "MarketplaceBrowseResult", "MarketplaceInfo", @@ -34716,6 +35171,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionApprovedKind", "PermissionDecisionCancelled", "PermissionDecisionCancelledKind", + "PermissionDecisionContext", "PermissionDecisionDeniedByContentExclusionPolicy", "PermissionDecisionDeniedByContentExclusionPolicyKind", "PermissionDecisionDeniedByPermissionRequestHook", @@ -34727,9 +35183,12 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser", "PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind", "PermissionDecisionKind", + "PermissionDecisionOutcome", "PermissionDecisionReject", "PermissionDecisionRejectKind", "PermissionDecisionRequest", + "PermissionDecisionSource", + "PermissionDecisionSurface", "PermissionDecisionUserNotAvailable", "PermissionDecisionUserNotAvailableKind", "PermissionLocationAddToolApprovalParams", @@ -34985,6 +35444,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "ServerInstructionSourceList", "ServerInstructionsApi", "ServerLlmInferenceApi", + "ServerManagedSettingsApi", "ServerMcpApi", "ServerMcpConfigApi", "ServerModelsApi", @@ -35071,6 +35531,8 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "SessionListFilter", "SessionLoadDeferredRepoHooksResult", "SessionLogLevel", + "SessionManagedPermissions", + "SessionManagedSettings", "SessionMcpAppsCallToolResult", "SessionMetadataSnapshot", "SessionModelList", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 6978a5a6d..d11317a82 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1208,13 +1208,14 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionManagedSettingsResolvedData: - "Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes." + "Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes." bypass_permissions_disabled: bool device_managed: bool fail_closed: bool managed_keys: list[str] server_managed: bool source: ManagedSettingsResolvedSource + client_managed: bool | None = None permissions_allow_intersected: bool | None = None settings: Any = None @@ -1227,6 +1228,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys = from_list(from_str, obj.get("managedKeys")) server_managed = from_bool(obj.get("serverManaged")) source = parse_enum(ManagedSettingsResolvedSource, obj.get("source")) + client_managed = from_union([from_none, from_bool], obj.get("clientManaged")) permissions_allow_intersected = from_union([from_none, from_bool], obj.get("permissionsAllowIntersected")) settings = obj.get("settings") return SessionManagedSettingsResolvedData( @@ -1236,6 +1238,7 @@ def from_dict(obj: Any) -> "SessionManagedSettingsResolvedData": managed_keys=managed_keys, server_managed=server_managed, source=source, + client_managed=client_managed, permissions_allow_intersected=permissions_allow_intersected, settings=settings, ) @@ -1248,6 +1251,8 @@ def to_dict(self) -> dict: result["managedKeys"] = from_list(from_str, self.managed_keys) result["serverManaged"] = from_bool(self.server_managed) result["source"] = to_enum(ManagedSettingsResolvedSource, self.source) + if self.client_managed is not None: + result["clientManaged"] = from_union([from_none, from_bool], self.client_managed) if self.permissions_allow_intersected is not None: result["permissionsAllowIntersected"] = from_union([from_none, from_bool], self.permissions_allow_intersected) if self.settings is not None: @@ -7784,6 +7789,7 @@ class SkillsLoadedSkill: source: SkillSource user_invocable: bool argument_hint: str | None = None + command_name: str | None = None path: str | None = None @staticmethod @@ -7795,6 +7801,7 @@ def from_dict(obj: Any) -> "SkillsLoadedSkill": source = parse_enum(SkillSource, obj.get("source")) user_invocable = from_bool(obj.get("userInvocable")) argument_hint = from_union([from_none, from_str], obj.get("argumentHint")) + command_name = from_union([from_none, from_str], obj.get("commandName")) path = from_union([from_none, from_str], obj.get("path")) return SkillsLoadedSkill( description=description, @@ -7803,6 +7810,7 @@ def from_dict(obj: Any) -> "SkillsLoadedSkill": source=source, user_invocable=user_invocable, argument_hint=argument_hint, + command_name=command_name, path=path, ) @@ -7815,6 +7823,8 @@ def to_dict(self) -> dict: result["userInvocable"] = from_bool(self.user_invocable) if self.argument_hint is not None: result["argumentHint"] = from_union([from_none, from_str], self.argument_hint) + if self.command_name is not None: + result["commandName"] = from_union([from_none, from_str], self.command_name) if self.path is not None: result["path"] = from_union([from_none, from_str], self.path) return result @@ -10134,12 +10144,16 @@ class ManagedSettingsEnforcedEscalation(Enum): class ManagedSettingsResolvedSource(Enum): - "Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale)" - # Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + "Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance." + # Only the server/account channel contributed. SERVER = "server" - # Device-level MDM policy discovered from plist/registry/file (lower authority). + # Only the device MDM/plist/registry/file channel contributed. DEVICE = "device" - # No managed policy is in force (no layer contributed). + # Only session-local SDK-host injection contributed. + CLIENT = "client" + # More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + MIXED = "mixed" + # No managed policy is in force (no channel contributed). NONE = "none" diff --git a/rust/examples/manual_tool_resume.rs b/rust/examples/manual_tool_resume.rs index 85add798a..ad8ad5a04 100644 --- a/rust/examples/manual_tool_resume.rs +++ b/rust/examples/manual_tool_resume.rs @@ -113,6 +113,7 @@ async fn main() -> Result<(), Box> { .rpc() .permissions() .handle_pending_permission_request(PermissionDecisionRequest { + decision_context: None, request_id: permission.request_id, result: PermissionDecision::ApproveOnce(PermissionDecisionApproveOnce { approved_interactively: None, diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index b64a13c4a..c93f34cf6 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -62,6 +62,8 @@ pub mod rpc_methods { pub const EXTENSIONS_ENABLE: &str = "extensions.enable"; /// `extensions.disable` pub const EXTENSIONS_DISABLE: &str = "extensions.disable"; + /// `registerExtensionLaunchProvider` + pub const REGISTEREXTENSIONLAUNCHPROVIDER: &str = "registerExtensionLaunchProvider"; /// `plugins.list` pub const PLUGINS_LIST: &str = "plugins.list"; /// `plugins.install` @@ -108,6 +110,8 @@ pub mod rpc_methods { pub const USER_SETTINGS_GET: &str = "user.settings.get"; /// `user.settings.set` pub const USER_SETTINGS_SET: &str = "user.settings.set"; + /// `managedSettings.read` + pub const MANAGEDSETTINGS_READ: &str = "managedSettings.read"; /// `runtime.shutdown` pub const RUNTIME_SHUTDOWN: &str = "runtime.shutdown"; /// `sessionFs.setProvider` @@ -3706,6 +3710,62 @@ pub struct ExtensionContextPushInput { pub r#type: ExtensionContextPushInputType, } +/// Opaque integrator-owned process launch profile for one extension entrypoint. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExtensionLaunchProfile { + /// Opaque integrator-defined arguments passed to the executable. The runtime does not append the extension entrypoint. + pub args: Vec, + /// Opaque integrator-defined environment variables. Runtime-owned COPILOT_SDK_PATH, SESSION_ID, and COPILOT_EXTENSION_PARENT_PID values take precedence. + pub env: HashMap, + /// Executable used to launch the extension entrypoint. + pub executable: String, +} + +/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExtensionLaunchProviderResolveRequest { + /// Source-qualified extension identifier. + pub id: String, + /// Absolute path to the discovered extension entrypoint. + pub module_path: String, + /// Human-readable extension name. + pub name: String, + /// Discovery source for the extension entrypoint. + pub source: ExtensionSource, +} + +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExtensionLaunchProviderResolveResult { + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + #[serde(skip_serializing_if = "Option::is_none")] + pub launch: Option, +} + /// Extensions discovered for the session, with their current status. /// ///
@@ -5904,6 +5964,25 @@ pub struct LspInitializeRequest { pub working_directory: Option, } +/// Validated device-managed settings discovered before a session exists. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ManagedSettingsReadResult { + /// Discovery or validation error text when managed settings could not be read safely. + #[serde(skip_serializing_if = "Option::is_none")] + pub error_message: Option, + /// Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. + #[serde(skip_serializing_if = "Option::is_none")] + pub settings_json: Option, +} + /// Result of registering a new marketplace. /// ///
@@ -7605,6 +7684,26 @@ pub struct MemoryConfiguration { pub enabled: bool, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MetadataContextAttributionResultContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -7636,10 +7735,24 @@ pub struct MetadataContextAttributionResultContextAttributionEntriesItem { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MetadataContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: MetadataContextAttributionResultContextAttributionCategories, /// Successful compaction history for the session. pub compactions: MetadataContextAttributionResultContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -9262,6 +9375,25 @@ pub struct PermissionDecisionDeniedByPermissionRequestHook { pub message: Option, } +/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PermissionDecisionContext { + /// Disposition of the permission request as observed by the responding client. + pub outcome: PermissionDecisionOutcome, + /// Controlled reason or actor responsible for the response. + pub source: PermissionDecisionSource, + /// Client surface that submitted the response. + pub surface: PermissionDecisionSurface, +} + /// Pending permission request ID and the decision to apply (approve/reject and scope). /// ///
@@ -9273,6 +9405,9 @@ pub struct PermissionDecisionDeniedByPermissionRequestHook { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PermissionDecisionRequest { + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + #[serde(skip_serializing_if = "Option::is_none")] + pub decision_context: Option, /// Request ID of the pending permission request pub request_id: RequestId, /// The client's response to the pending permission prompt @@ -12337,9 +12472,9 @@ pub struct SandboxConfig { /// Whether to auto-add the current working directory to readwritePaths. Default: true. #[serde(skip_serializing_if = "Option::is_none")] pub add_current_working_directory: Option, - /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without exporting CARGO_HOME/GOPATH/etc. Default: true (enabled by default; set to false to opt out). + /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). #[serde(skip_serializing_if = "Option::is_none")] - pub allow_dev_tool_caches: Option, + pub allow_dev_tool_access: Option, /// Whether sandboxing is enabled for the session. pub enabled: bool, /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). @@ -12842,6 +12977,9 @@ pub struct ServerSkill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled (based on global config) @@ -12940,6 +13078,26 @@ pub struct SessionBulkDeleteResult { pub freed_bytes: HashMap, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -12978,10 +13136,24 @@ pub struct SessionContextAttributionEntriesItem { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: SessionContextAttributionCategories, /// Successful compaction history for the session. pub compactions: SessionContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -13793,6 +13965,46 @@ pub struct SessionLoadDeferredRepoHooksResult { pub startup_prompts: Vec, } +/// Enterprise permission policy expressed with the runtime's managed permission-rule syntax. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionManagedPermissions { + /// Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. + #[serde(skip_serializing_if = "Option::is_none")] + pub allow: Option>, + /// Permission rules that require explicit human approval. + #[serde(skip_serializing_if = "Option::is_none")] + pub ask: Option>, + /// Permission rules that block matching operations. Deny has highest precedence. + #[serde(skip_serializing_if = "Option::is_none")] + pub deny: Option>, + /// When set to `disable`, prevents bypass/allow-all permission modes. + #[serde(skip_serializing_if = "Option::is_none")] + pub disable_bypass_permissions_mode: Option, +} + +/// Managed settings an SDK host may inject at session startup. Only permissions are accepted in this initial contract. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionManagedSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + /// Public-facing projection of workspace metadata for SDK / TUI consumers #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -14087,6 +14299,9 @@ pub struct SessionOpenOptions { /// Instruction source IDs disabled for this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_instruction_sources: Option>, + /// MCP server names disabled for this session. Disabled servers are not started or authenticated on create or cold resume. + #[serde(skip_serializing_if = "Option::is_none")] + pub disabled_mcp_servers: Option>, /// Skill IDs disabled for this session. #[serde(skip_serializing_if = "Option::is_none")] pub disabled_skills: Option>, @@ -14155,6 +14370,9 @@ pub struct SessionOpenOptions { /// Identifier sent to LSP-style integrations. #[serde(skip_serializing_if = "Option::is_none")] pub lsp_client_name: Option, + /// Permissions-only enterprise policy injected by the SDK host at session create or resume. Composes restrictively with self-fetched and device policy and is not persisted. + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, /// Maximum decoded byte size of a single inline model-facing binary tool result persisted in session events (default 10 MB). #[serde(skip_serializing_if = "Option::is_none")] pub max_inline_binary_bytes: Option, @@ -15751,6 +15969,9 @@ pub struct Skill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled @@ -22068,6 +22289,26 @@ pub struct SessionMetadataGetContextAttributionParams { pub session_id: SessionId, } +/// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionMetadataGetContextAttributionResultContextAttributionCategories { + /// Output reserve plus post-blocking-threshold buffer. + pub buffer: i64, + /// Custom-instructions tokens (0 when none are configured). + pub custom_instructions: i64, + /// Remaining unused window capacity (clamped at 0). + pub free_space: i64, + /// MCP tool-definition tokens. + pub mcp_tools: i64, + /// Conversation (user/assistant/tool) message tokens. + pub messages: i64, + /// System prompt tokens, excluding custom instructions. + pub system_prompt: i64, + /// Non-MCP tool-definition tokens. + pub system_tools: i64, +} + /// Successful compaction history for the session. #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -22099,10 +22340,24 @@ pub struct SessionMetadataGetContextAttributionResultContextAttributionEntriesIt #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionMetadataGetContextAttributionResultContextAttribution { + /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + pub buffer_tokens: i64, + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + pub categories: SessionMetadataGetContextAttributionResultContextAttributionCategories, /// Successful compaction history for the session. pub compactions: SessionMetadataGetContextAttributionResultContextAttributionCompactions, + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + pub compaction_threshold: i64, /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. pub entries: Vec, + /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. + pub limit: i64, + /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. + pub model_id: String, + /// How `modelId` was chosen. Not a closed set — tolerate unknown values. Known values today: `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected model), `default` (a fallback before any model is known). + pub model_source: String, + /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + pub prompt_token_limit: i64, /// Total token count of the current context window the entries are measured against (system message + conversation messages + tool definitions — the same total reported by /context). Divide an entry's `tokens` by this to derive its share. pub total_tokens: i64, } @@ -24223,6 +24478,23 @@ pub enum DebugCollectLogsResultKind { Unknown, } +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum DisableBypassPermissionsMode { + #[serde(rename = "disable")] + Disable, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Persisted extension discovery source /// ///
@@ -26224,6 +26496,87 @@ pub enum PermissionDecision { DeniedByPermissionRequestHook(PermissionDecisionDeniedByPermissionRequestHook), } +/// Disposition of a permission request as observed by the responding client. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionDecisionOutcome { + /// The request was approved automatically without a new human decision. + #[serde(rename = "auto_approved")] + AutoApproved, + /// The request was denied without an interactive user decision; source records why. + #[serde(rename = "autopilot_denied")] + AutopilotDenied, + /// The response came from an interactive user prompt. + #[serde(rename = "prompted_user")] + PromptedUser, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Controlled reason or actor responsible for a permission response. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionDecisionSource { + /// The response followed the auto-approval judge recommendation. + #[serde(rename = "judge_recommendation")] + JudgeRecommendation, + /// A human supplied the response through an interactive prompt. + #[serde(rename = "human_response")] + HumanResponse, + /// The host applied a standing policy or override rather than a judge recommendation or human decision. + #[serde(rename = "host_policy")] + HostPolicy, + /// The host denied the request because no interactive user response was available. + #[serde(rename = "unattended_fallback")] + UnattendedFallback, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + +/// Client surface that submitted a permission response. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub enum PermissionDecisionSurface { + /// The interactive Copilot CLI terminal UI. + #[serde(rename = "tui")] + Tui, + /// The non-interactive Copilot CLI prompt mode. + #[serde(rename = "prompt_mode")] + PromptMode, + /// The Copilot App client. + #[serde(rename = "copilot_app")] + CopilotApp, + /// A generic Copilot SDK client. + #[serde(rename = "sdk")] + Sdk, + /// Unknown variant for forward compatibility. + #[default] + #[serde(other)] + Unknown, +} + /// Approval scoped to specific command identifiers. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum PermissionsLocationsAddToolApprovalDetailsCommandsKind { diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 02404c112..db69aa1e8 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -71,6 +71,13 @@ impl<'a> ClientRpc<'a> { } } + /// `managedSettings.*` sub-namespace. + pub fn managed_settings(&self) -> ClientRpcManagedSettings<'a> { + ClientRpcManagedSettings { + client: self.client, + } + } + /// `mcp.*` sub-namespace. pub fn mcp(&self) -> ClientRpcMcp<'a> { ClientRpcMcp { @@ -196,6 +203,29 @@ impl<'a> ClientRpc<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Registers the calling SDK client as the per-entrypoint extension launch provider. Call before creating any sessions. When omitted, the runtime temporarily falls back to its built-in Node launcher for backward compatibility. + /// + /// Wire method: `registerExtensionLaunchProvider`. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn register_extension_launch_provider(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::REGISTEREXTENSIONLAUNCHPROVIDER, + Some(wire_params), + ) + .await?; + Ok(()) + } } /// `account.*` RPCs. @@ -756,6 +786,38 @@ impl<'a> ClientRpcLlmInference<'a> { } } +/// `managedSettings.*` RPCs. +#[derive(Clone, Copy)] +pub struct ClientRpcManagedSettings<'a> { + pub(crate) client: &'a Client, +} + +impl<'a> ClientRpcManagedSettings<'a> { + /// Discovers device-managed settings from production MDM and managed-file sources, validates them against the runtime-owned managed-settings schema, and returns the canonical JSON without requiring a session. + /// + /// Wire method: `managedSettings.read`. + /// + /// # Returns + /// + /// Validated device-managed settings discovered before a session exists. + /// + ///
+ /// + /// **Experimental.** This API is part of an experimental wire-protocol surface + /// and may change or be removed in future SDK or CLI releases. Pin both the + /// SDK and CLI versions if your code depends on it. + /// + ///
+ pub async fn read(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::MANAGEDSETTINGS_READ, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + /// `mcp.*` RPCs. #[derive(Clone, Copy)] pub struct ClientRpcMcp<'a> { diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index ef3c395b1..f5bfab83f 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -4447,7 +4447,7 @@ pub struct SessionAutoModeResolvedData { pub sticky_override: Option, } -/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes. +/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and which channels contributed, so SDK clients can show users what is enterprise-managed. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted. Device values take precedence over server values per ordinary key, while permissions compose restrictively across device, server, and SDK-client layers. The account-scoped `getManagedSettings()` API does not include session-local client injection. Marked experimental while the managed-settings surface stabilizes. /// ///
/// @@ -4460,13 +4460,16 @@ pub struct SessionAutoModeResolvedData { pub struct SessionManagedSettingsResolvedData { /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true. pub bypass_permissions_disabled: bool, - /// Whether the device (MDM/plist/registry/file) managed-settings layer was present + /// Whether a session-local permissions layer injected by the SDK host was present + #[serde(skip_serializing_if = "Option::is_none")] + pub client_managed: Option, + /// Whether an actual device MDM/plist/registry/file managed-settings layer was present pub device_managed: bool, /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent. pub fail_closed: bool, /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force. pub managed_keys: Vec, - /// Whether server and device each supplied a permission allowlist, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. + /// Whether at least two managed sources supplied permission allowlists, so enforcement intersects them and the flattened settings payload omits `permissions.allow`. #[serde(skip_serializing_if = "Option::is_none")] pub permissions_allow_intersected: Option, /// Whether the server (account/org) managed-settings layer was present @@ -4474,7 +4477,7 @@ pub struct SessionManagedSettingsResolvedData { /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. #[serde(skip_serializing_if = "Option::is_none")] pub settings: Option, - /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force + /// Channel summary: `server`, `device`, or `client` when exactly one channel contributed; `mixed` when multiple channels contributed; otherwise `none`. Consult the per-channel booleans for exact provenance. pub source: ManagedSettingsResolvedSource, } @@ -4617,6 +4620,9 @@ pub struct SkillsLoadedSkill { /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field #[serde(skip_serializing_if = "Option::is_none")] pub argument_hint: Option, + /// Canonical slash command name used to invoke the skill, without the leading '/' + #[serde(skip_serializing_if = "Option::is_none")] + pub command_name: Option, /// Description of what the skill does pub description: String, /// Whether the skill is currently enabled @@ -6335,16 +6341,22 @@ pub enum AutoModeResolvedReasoningBucket { Unknown, } -/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale) +/// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ManagedSettingsResolvedSource { - /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority). + /// Only the server/account channel contributed. #[serde(rename = "server")] Server, - /// Device-level MDM policy discovered from plist/registry/file (lower authority). + /// Only the device MDM/plist/registry/file channel contributed. #[serde(rename = "device")] Device, - /// No managed policy is in force (no layer contributed). + /// Only session-local SDK-host injection contributed. + #[serde(rename = "client")] + Client, + /// More than one channel contributed. Ordinary keys resolve device over server per key, while permissions compose restrictively across all present layers. + #[serde(rename = "mixed")] + Mixed, + /// No managed policy is in force (no channel contributed). #[serde(rename = "none")] None, /// Unknown variant for forward compatibility. diff --git a/rust/tests/e2e/rpc_tasks_and_handlers.rs b/rust/tests/e2e/rpc_tasks_and_handlers.rs index b3010ab78..b046687f4 100644 --- a/rust/tests/e2e/rpc_tasks_and_handlers.rs +++ b/rust/tests/e2e/rpc_tasks_and_handlers.rs @@ -404,6 +404,7 @@ async fn should_return_expected_results_for_missing_pending_handler_requestids() .rpc() .permissions() .handle_pending_permission_request(PermissionDecisionRequest { + decision_context: None, request_id: request_id.into(), result, }) diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 4702dbba6..14f382f7c 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.78.tgz", - "integrity": "sha512-jn+8HLZC3R7d6K1/1g9L1iWNKzBVS3JdVcx40r3aWyS5r+MLV1OPNp0fo5OfRMCDIm3NmEaaoqypi9sQkCXuiQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", + "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.78", - "@github/copilot-darwin-x64": "1.0.78", - "@github/copilot-linux-arm64": "1.0.78", - "@github/copilot-linux-x64": "1.0.78", - "@github/copilot-linuxmusl-arm64": "1.0.78", - "@github/copilot-linuxmusl-x64": "1.0.78", - "@github/copilot-win32-arm64": "1.0.78", - "@github/copilot-win32-x64": "1.0.78" + "@github/copilot-darwin-arm64": "1.0.79-5", + "@github/copilot-darwin-x64": "1.0.79-5", + "@github/copilot-linux-arm64": "1.0.79-5", + "@github/copilot-linux-x64": "1.0.79-5", + "@github/copilot-linuxmusl-arm64": "1.0.79-5", + "@github/copilot-linuxmusl-x64": "1.0.79-5", + "@github/copilot-win32-arm64": "1.0.79-5", + "@github/copilot-win32-x64": "1.0.79-5" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.78.tgz", - "integrity": "sha512-P11+VyWg8ad0WlywGtO2d7AxqTLJv4hkUicFg6Ycth5lfk00aCu/74YOOZSPO6C2bBBJhAza7oAdmauM6KEojw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", + "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.78.tgz", - "integrity": "sha512-stimP3WDFs2GU8nJzTJbtRpZViV4bsf80yg7QrFq+G4RISQ3Nihg/3/H0U6UQF1+txMJ/Ohmb5RFYxSw1Hj2sw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", + "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.78.tgz", - "integrity": "sha512-K31PRKGTm252V1Lof7ypjg283R2QSm3BgoCvZfX2taos4wqC3SaTozSQKwW3dgrAx7A3G3SGEoilVCNqfigdZA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", + "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.78.tgz", - "integrity": "sha512-QK3oMtAn9dIv+1u1kx0xNpZNtZxdI+uZVIyLl7myp+Oh2Uj8BLagVv6a7uP0cDphO3TgfIdlvpepCe5MIcx0fw==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", + "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.78.tgz", - "integrity": "sha512-F/0cTMsz6ug4yiXn3RKaCAMsLR261U5Njb6G9Y/HeAI7ES/tKEo2t5SHuvgXaIH4mYiZsRvfDKdX7c0WgBX/Jg==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", + "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.78.tgz", - "integrity": "sha512-YMaJaeBGbArGAFYel+yFaFW/0rFgh0Oqki2f2mUtlonTX/xHr8EB4+mTnMJkHYMFy4gOTC3OtSEEe1NaW/cBXQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", + "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.78.tgz", - "integrity": "sha512-ktDkFXaaecEKD3hpM6ydM9lKOdoCfsQsXCmzLzE7DCmSpbbMCdfPfWfZ7MOclmKmpZ5/MNfr4U2l8CUqGerzYA==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", + "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.78.tgz", - "integrity": "sha512-Gd8l2T4eqYEWlOEPd0SZznQ+YYgYrwOkE0QXodMkhCBbPdgu/uTzb7mnISWwnVAgqs7pONdF1GOpHkTo+ay8CQ==", + "version": "1.0.79-5", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", + "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index f1556534d..4cb25e106 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.78", + "@github/copilot": "^1.0.79-5", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From b143f06c1f8e7f2dc3aa8a2f33b1d0f9018f5413 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 22:04:19 -0400 Subject: [PATCH 43/72] Update @github/copilot to 1.0.79-6 (#2287) - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +++++++++++++------------- java/scripts/codegen/package.json | 2 +- nodejs/package-lock.json | 72 +++++++++++++------------- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- test/harness/package-lock.json | 72 +++++++++++++------------- test/harness/package.json | 2 +- 8 files changed, 113 insertions(+), 113 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 8080e2c49..ea468a75e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.79-5 + ^1.0.79-6 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index bd41d4b12..5b3197f6f 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", - "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", + "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-5", - "@github/copilot-darwin-x64": "1.0.79-5", - "@github/copilot-linux-arm64": "1.0.79-5", - "@github/copilot-linux-x64": "1.0.79-5", - "@github/copilot-linuxmusl-arm64": "1.0.79-5", - "@github/copilot-linuxmusl-x64": "1.0.79-5", - "@github/copilot-win32-arm64": "1.0.79-5", - "@github/copilot-win32-x64": "1.0.79-5" + "@github/copilot-darwin-arm64": "1.0.79-6", + "@github/copilot-darwin-x64": "1.0.79-6", + "@github/copilot-linux-arm64": "1.0.79-6", + "@github/copilot-linux-x64": "1.0.79-6", + "@github/copilot-linuxmusl-arm64": "1.0.79-6", + "@github/copilot-linuxmusl-x64": "1.0.79-6", + "@github/copilot-win32-arm64": "1.0.79-6", + "@github/copilot-win32-x64": "1.0.79-6" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", - "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", + "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", - "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", + "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", - "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", + "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", - "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", + "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", - "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", + "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", - "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", + "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", - "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", + "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", - "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", + "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 3e0e5af6a..bb8936e75 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 2416d147c..cd571ced1 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", - "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", + "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-5", - "@github/copilot-darwin-x64": "1.0.79-5", - "@github/copilot-linux-arm64": "1.0.79-5", - "@github/copilot-linux-x64": "1.0.79-5", - "@github/copilot-linuxmusl-arm64": "1.0.79-5", - "@github/copilot-linuxmusl-x64": "1.0.79-5", - "@github/copilot-win32-arm64": "1.0.79-5", - "@github/copilot-win32-x64": "1.0.79-5" + "@github/copilot-darwin-arm64": "1.0.79-6", + "@github/copilot-darwin-x64": "1.0.79-6", + "@github/copilot-linux-arm64": "1.0.79-6", + "@github/copilot-linux-x64": "1.0.79-6", + "@github/copilot-linuxmusl-arm64": "1.0.79-6", + "@github/copilot-linuxmusl-x64": "1.0.79-6", + "@github/copilot-win32-arm64": "1.0.79-6", + "@github/copilot-win32-x64": "1.0.79-6" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", - "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", + "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", - "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", + "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", - "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", + "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", - "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", + "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", - "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", + "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", - "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", + "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", - "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", + "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", - "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", + "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 3aa1a72c7..5becc3d3c 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 7a559a612..5794a0032 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 14f382f7c..b55238c17 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-5.tgz", - "integrity": "sha512-gQj87QGcWQpAx2YcBcBZ15q5ITA9y9aLDu0mIZb2daIRQqIK8VKtmY70G5d271eqrztTqd2b3Nu0mlB64E5ufg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", + "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-5", - "@github/copilot-darwin-x64": "1.0.79-5", - "@github/copilot-linux-arm64": "1.0.79-5", - "@github/copilot-linux-x64": "1.0.79-5", - "@github/copilot-linuxmusl-arm64": "1.0.79-5", - "@github/copilot-linuxmusl-x64": "1.0.79-5", - "@github/copilot-win32-arm64": "1.0.79-5", - "@github/copilot-win32-x64": "1.0.79-5" + "@github/copilot-darwin-arm64": "1.0.79-6", + "@github/copilot-darwin-x64": "1.0.79-6", + "@github/copilot-linux-arm64": "1.0.79-6", + "@github/copilot-linux-x64": "1.0.79-6", + "@github/copilot-linuxmusl-arm64": "1.0.79-6", + "@github/copilot-linuxmusl-x64": "1.0.79-6", + "@github/copilot-win32-arm64": "1.0.79-6", + "@github/copilot-win32-x64": "1.0.79-6" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-5.tgz", - "integrity": "sha512-pR/ZrznLn6oOUcIPPgzaOOHKDLmeznV1yHwzkmTrqJVwlFgfFYeqBGTD3YwAqSmGAtXdDk+16o3j0mCg0m3+9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", + "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-5.tgz", - "integrity": "sha512-L828i0YUiI7IAACsmMnKg7LKrgzu5KqgOyA72BmxPmIYUpRpIxYt8QU8oGeej52de+I6zRYUCSWkLAkKLa6oFw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", + "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-5.tgz", - "integrity": "sha512-r3GT8kHGOhxbf+QaHp4vOQnJTnrreYOM+eTkgyiVrDe9GR5RKOO7XHjr8F9ob29UP4qpci/2LpgcYIJ9yiszgg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", + "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-5.tgz", - "integrity": "sha512-as7EcVDOC7TtoO9+bX8NrQsACPdPKg6t0HTaAaa7oRAftalGX8ZGhGouBOL6v13vpR2xtLaRRaP3YMwo//XQ9A==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", + "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-5.tgz", - "integrity": "sha512-znO0FerQz6kUj46GqjGACR+ernRrAL3iymYo3ipGdf/E71fZnhyZ4BReh0ajRk6b2O5b7dbItk6K0K3Vr/Wn4g==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", + "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-5.tgz", - "integrity": "sha512-6hDWKHNEyMwvsPwHYp+Yz3QIojFfheVzhpgz+zZde1uLSf7okiV3qP5Uyuwixv4qoissxnD+2o8eL6VAJl3fsg==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", + "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-5.tgz", - "integrity": "sha512-kvt3YrwZ4/VhAMsUau/T1SCzg4e5/ki7gBF2bxJqszh3B+7DVDXZB/jaUPexLUAbGhxSAzB9c02X1tgzt3m4nw==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", + "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-5", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-5.tgz", - "integrity": "sha512-e+5DJhN92vMvqrK0O4hV4KTCe5AP9F75K3PbGlSzEAjHhaXYxvPcx3hsL85SEMAoBrXItx64kHQMQm33Jo9K+Q==", + "version": "1.0.79-6", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", + "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 4cb25e106..efb64a56d 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.79-5", + "@github/copilot": "^1.0.79-6", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From 46d39b9f06a1eead55a133fcd237ac2d15b7ce57 Mon Sep 17 00:00:00 2001 From: Chuck Lantz Date: Thu, 6 Aug 2026 22:34:14 -0500 Subject: [PATCH 44/72] fix(rust): recover JSON-RPC lone surrogates (#2283) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- rust/src/jsonrpc.rs | 84 ++++++++++++++++++++++- rust/tests/jsonrpc_test.rs | 137 ++++++++++++++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 3 deletions(-) diff --git a/rust/src/jsonrpc.rs b/rust/src/jsonrpc.rs index fbdc96505..25a405080 100644 --- a/rust/src/jsonrpc.rs +++ b/rust/src/jsonrpc.rs @@ -169,6 +169,68 @@ impl JsonRpcResponse { const CONTENT_LENGTH_HEADER: &str = "Content-Length: "; +/// Rewrites unpaired UTF-16 surrogate escapes to `\uFFFD`. +/// +/// Returns `None` when the body contains no unpaired surrogate, so valid +/// frames do not incur a repair allocation. +fn repair_lone_surrogates(body: &[u8]) -> Option> { + fn hex_escape_at(body: &[u8], index: usize) -> Option { + let digits = body.get(index + 2..index + 6)?; + let text = std::str::from_utf8(digits).ok()?; + u16::from_str_radix(text, 16).ok() + } + + let mut repaired = None; + let mut in_string = false; + let mut index = 0; + + while index < body.len() { + let byte = body[index]; + + if !in_string { + in_string = byte == b'"'; + index += 1; + continue; + } + + match byte { + b'"' => { + in_string = false; + index += 1; + } + // Consume non-Unicode escapes whole so an escaped backslash cannot + // be mistaken for the start of a surrogate escape. + b'\\' if body.get(index + 1) != Some(&b'u') => index += 2, + b'\\' => { + let Some(unit) = hex_escape_at(body, index) else { + index += 2; + continue; + }; + + let is_pair = (0xD800..0xDC00).contains(&unit) + && body.get(index + 6) == Some(&b'\\') + && body.get(index + 7) == Some(&b'u') + && hex_escape_at(body, index + 6) + .is_some_and(|low| (0xDC00..0xE000).contains(&low)); + + if is_pair { + index += 12; + continue; + } + + if (0xD800..0xE000).contains(&unit) { + let output = repaired.get_or_insert_with(|| body.to_vec()); + output[index..index + 6].copy_from_slice(br"\ufffd"); + } + index += 6; + } + _ => index += 1, + } + } + + repaired +} + /// One framed JSON-RPC message handed to the writer actor. /// /// `frame` is the fully serialized bytes (header + body); the caller pays @@ -428,8 +490,26 @@ impl JsonRpcClient { let mut body = vec![0u8; length]; reader.read_exact(&mut body).await?; - let message: JsonRpcMessage = serde_json::from_slice(&body)?; - Ok(Some(message)) + match serde_json::from_slice::(&body) { + Ok(message) => Ok(Some(message)), + Err(error) => { + // Dropping an undecodable frame could leave its pending + // request waiting forever because this layer has no timeout. + match repair_lone_surrogates(&body) + .and_then(|repaired| serde_json::from_slice::(&repaired).ok()) + { + Some(message) => { + warn!( + error = %error, + length, + "recovered JSON-RPC frame containing unpaired UTF-16 surrogates" + ); + Ok(Some(message)) + } + None => Err(error.into()), + } + } + } } /// Send a JSON-RPC request and wait for the matching response. diff --git a/rust/tests/jsonrpc_test.rs b/rust/tests/jsonrpc_test.rs index 7f7d43213..1735067c3 100644 --- a/rust/tests/jsonrpc_test.rs +++ b/rust/tests/jsonrpc_test.rs @@ -2,7 +2,7 @@ #![allow(clippy::unwrap_used)] use github_copilot_sdk::test_support::{JsonRpcClient, JsonRpcNotification, JsonRpcRequest}; -use tokio::io::{AsyncWrite, AsyncWriteExt, duplex}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, duplex}; use tokio::sync::{broadcast, mpsc}; /// Write a Content-Length framed JSON-RPC message to a writer. @@ -13,6 +13,28 @@ async fn write_framed(writer: &mut (impl AsyncWrite + Unpin), body: &[u8]) { writer.flush().await.unwrap(); } +async fn read_framed(reader: &mut (impl AsyncRead + Unpin)) -> Vec { + let mut header = String::new(); + loop { + let mut byte = [0u8; 1]; + reader.read_exact(&mut byte).await.unwrap(); + header.push(byte[0] as char); + if header.ends_with("\r\n\r\n") { + break; + } + } + + let length = header + .trim() + .strip_prefix("Content-Length: ") + .unwrap() + .parse() + .unwrap(); + let mut body = vec![0u8; length]; + reader.read_exact(&mut body).await.unwrap(); + body +} + #[tokio::test] async fn request_response_round_trip() { // duplex: client_write → server_read, server_write → client_read @@ -410,3 +432,116 @@ async fn send_request_cancellation_does_not_leak_pending() { assert_eq!(response.result.unwrap()["ok"], true); server_task.await.unwrap(); } + +#[test] +fn lone_surrogate_yields_unexpected_end_of_hex_escape() { + let error = serde_json::from_slice::(br#""\ud83d""#).unwrap_err(); + + assert_eq!( + error.to_string(), + "unexpected end of hex escape at line 1 column 8" + ); +} + +#[tokio::test] +async fn lone_surrogate_frame_is_recovered_without_closing_connection() { + let (client_write, mut server_read) = duplex(4096); + let (mut server_write, client_read) = duplex(4096); + let (notification_tx, _) = broadcast::channel(16); + let (request_tx, _) = mpsc::unbounded_channel(); + let client = JsonRpcClient::new(client_write, client_read, notification_tx, request_tx); + + let server_task = tokio::spawn(async move { + let request: JsonRpcRequest = + serde_json::from_slice(&read_framed(&mut server_read).await).unwrap(); + let response = format!( + r#"{{"jsonrpc":"2.0","id":{},"result":{{"name":"invalid \ud83d value"}}}}"#, + request.id + ); + write_framed(&mut server_write, response.as_bytes()).await; + + let request: JsonRpcRequest = + serde_json::from_slice(&read_framed(&mut server_read).await).unwrap(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": request.id, + "result": {"name": "still connected"} + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + }); + + let response = client.send_request("models.list", None).await.unwrap(); + assert_eq!( + response.result.unwrap()["name"], + serde_json::json!("invalid \u{FFFD} value") + ); + + let response = client.send_request("account.getQuota", None).await.unwrap(); + assert_eq!( + response.result.unwrap()["name"], + serde_json::json!("still connected") + ); + server_task.await.unwrap(); +} + +#[tokio::test] +async fn unrepairable_frame_remains_fatal() { + let (client_write, mut server_read) = duplex(4096); + let (mut server_write, client_read) = duplex(4096); + let (notification_tx, _) = broadcast::channel(16); + let (request_tx, _) = mpsc::unbounded_channel(); + let client = JsonRpcClient::new(client_write, client_read, notification_tx, request_tx); + + let server_task = tokio::spawn(async move { + let request: JsonRpcRequest = + serde_json::from_slice(&read_framed(&mut server_read).await).unwrap(); + let response = format!( + r#"{{"jsonrpc":"2.0","id":{},"result":{{"surrogate":"\ud83d","escape":"\q"}}}}"#, + request.id + ); + write_framed(&mut server_write, response.as_bytes()).await; + }); + + let error = tokio::time::timeout( + std::time::Duration::from_secs(2), + client.send_request("models.list", None), + ) + .await + .expect("unrepairable frame did not terminate the pending request") + .unwrap_err(); + + assert_eq!(error.to_string(), "request cancelled"); + assert!(error.is_transport_failure()); + server_task.await.unwrap(); +} + +#[tokio::test] +async fn valid_pairs_and_escaped_backslashes_are_untouched() { + let (client_write, mut server_read) = duplex(4096); + let (mut server_write, client_read) = duplex(4096); + let (notification_tx, _) = broadcast::channel(16); + let (request_tx, _) = mpsc::unbounded_channel(); + let client = JsonRpcClient::new(client_write, client_read, notification_tx, request_tx); + + let server_task = tokio::spawn(async move { + let request: JsonRpcRequest = + serde_json::from_slice(&read_framed(&mut server_read).await).unwrap(); + let response = format!( + r#"{{"jsonrpc":"2.0","id":{},"result":{{"emoji":"\ud83d\ude00","path":"C:\\ud83d","invalid":"\ud83d"}}}}"#, + request.id + ); + write_framed(&mut server_write, response.as_bytes()).await; + }); + + let result = client + .send_request("models.list", None) + .await + .unwrap() + .result + .unwrap(); + + assert_eq!(result["emoji"], serde_json::json!("😀")); + assert_eq!(result["path"], serde_json::json!(r"C:\ud83d")); + assert_eq!(result["invalid"], serde_json::json!("\u{FFFD}")); + server_task.await.unwrap(); +} From 846b34b3becccfd440f048339248d2e50a3dc57c Mon Sep 17 00:00:00 2001 From: joshspicer <23246594+joshspicer@users.noreply.github.com> Date: Thu, 6 Aug 2026 21:39:28 -0700 Subject: [PATCH 45/72] Add managed permission settings to session startup (#2139) * Add managed permission settings to session startup Regenerate RPC and session-event mirrors from @github/copilot 1.0.79-5 and serialize permissions-only managed settings on create and resume across all six SDKs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c85d8afb-8b62-4636-9408-7a3ba6a82931 * Harden managed permission settings contracts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c85d8afb-8b62-4636-9408-7a3ba6a82931 * chore: retrigger CI after Actions outage Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5d4d699-33e2-4a55-9d48-57d2e483dd3d --------- Copilot-Session: c85d8afb-8b62-4636-9408-7a3ba6a82931 Copilot-Session: d5d4d699-33e2-4a55-9d48-57d2e483dd3d Copilot-Session: 326a5a5a-aa12-4d85-8b86-c7e392cf1b23 --- CHANGELOG.md | 37 ++++ dotnet/src/Client.cs | 6 +- dotnet/src/Types.cs | 76 +++++++++ .../test/Unit/ClientSessionLifetimeTests.cs | 89 ++++++++++ .../Unit/SessionEventSerializationTests.cs | 61 +++++++ go/client.go | 10 +- go/client_test.go | 159 ++++++++++++++++++ go/session_event_serialization_test.go | 66 ++++++++ go/types.go | 52 ++++++ .../github/copilot/SessionRequestBuilder.java | 8 +- .../copilot/rpc/CreateSessionRequest.java | 17 ++ .../github/copilot/rpc/ManagedSettings.java | 34 ++++ .../rpc/ManagedSettingsPermissions.java | 90 ++++++++++ .../copilot/rpc/ResumeSessionConfig.java | 20 +++ .../copilot/rpc/ResumeSessionRequest.java | 17 ++ .../com/github/copilot/rpc/SessionConfig.java | 25 +++ .../github/copilot/ManagedSettingsTest.java | 90 ++++++++++ .../SessionEventDeserializationTest.java | 67 +++++++- nodejs/src/client.ts | 9 +- nodejs/src/index.ts | 2 + nodejs/src/types.ts | 64 +++++++ nodejs/test/client.test.ts | 98 +++++++++++ nodejs/test/session-event-types.test.ts | 45 +++++ python/copilot/__init__.py | 4 + python/copilot/client.py | 87 +++++++++- python/test_client.py | 96 ++++++++++- python/test_event_forward_compatibility.py | 38 +++++ rust/src/session.rs | 26 ++- rust/src/types.rs | 131 +++++++++++++++ rust/src/wire.rs | 4 + rust/tests/session_test.rs | 137 ++++++++++++++- 31 files changed, 1635 insertions(+), 30 deletions(-) create mode 100644 java/src/main/java/com/github/copilot/rpc/ManagedSettings.java create mode 100644 java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java create mode 100644 java/src/test/java/com/github/copilot/ManagedSettingsTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bf05e17..e9f22a3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,43 @@ All notable changes to the Copilot SDK are documented in this file. This changelog is automatically generated by an AI agent when stable releases are published. See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the full list. +## [Unreleased] + +### Feature: host-injected managed settings permissions + +Session create and resume accept a new optional `managedSettings` option that injects an enterprise permissions policy at session startup, alongside the existing `enableManagedSettings` self-fetch flag. The current contract is permissions-only: `disableBypassPermissionsMode` (the literal `"disable"`), plus `deny`, `ask`, and `allow` rule lists. The layer composes restrictively with any server- or device-level managed settings (deny/ask are unioned, every present allow list must admit a tool, and `disableBypassPermissionsMode` is deny-wins). + +This layer is startup-only and is not persisted with the session, so it must be re-supplied on resume to remain in effect; omitting it on resume clears the previously injected layer. It can be combined with `enableManagedSettings`. Host injection requires Copilot CLI `1.0.79-5` or later and does not require an SDK protocol version bump. + +The generated session-event types also expose truthful injected-policy provenance: `session.managed_settings_resolved` can report `source` as `client` or `mixed`, with optional `clientManaged` metadata. + +```ts +const session = await client.createSession({ + managedSettings: { + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["shell(rm*)"], + ask: ["write"], + }, + }, +}); +``` + +```cs +var session = await client.CreateSessionAsync(new SessionConfig +{ + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + DisableBypassPermissionsMode = DisableBypassPermissionsMode.Disable, + Deny = ["shell(rm*)"], + Ask = ["write"], + }, + }, +}); +``` + ## [v1.0.7](https://github.com/github/copilot-sdk/releases/tag/v1.0.7) (2026-07-16) ### Feature: in-process (FFI) transport diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 228b11560..2df1f05d1 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -785,7 +785,7 @@ private CopilotSession InitializeSession( session.RegisterTools(config.Tools ?? []); session.RegisterPermissionHandler( config.OnPermissionRequest, - config.EnableManagedSettings is true); + config.EnableManagedSettings is true || config.ManagedSettings is not null); session.RegisterMcpAuthHandler(config.OnMcpAuthRequest); session.RegisterCommands(config.Commands); session.RegisterElicitationHandler(config.OnElicitationRequest); @@ -1205,6 +1205,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, + ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, AdditionalDirectories: config.AdditionalDirectories); @@ -1425,6 +1426,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, GitHubMcpToolConfig: config.GitHubMcpToolConfig, + ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, AdditionalDirectories: config.AdditionalDirectories); @@ -2781,6 +2783,7 @@ internal record CreateSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, IList? AdditionalDirectories = null); @@ -2895,6 +2898,7 @@ internal record ResumeSessionRequest( OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, IList? AdditionalDirectories = null); diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 7489fbb4e..680955a01 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3044,6 +3044,70 @@ public sealed class GitHubMcpToolConfig public bool? DisableFormDeferral { get; set; } } +/// +/// Controls whether bypass-permissions mode is available in a managed session. +/// +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum DisableBypassPermissionsMode +{ + /// Turn off bypass-permissions mode. + [JsonStringEnumMemberName("disable")] + Disable +} + +/// +/// Permission rules injected as a managed-settings layer at session bootstrap. +/// All fields are optional; omitted fields impose no constraint from this layer. +/// +/// +/// This layer composes restrictively with any server- or device-level managed +/// settings: and rules are unioned across +/// layers, every present list must admit a tool for it to be +/// allowed, and is honored if any +/// layer sets it (deny-wins). +/// +public sealed class ManagedSettingsPermissions +{ + /// + /// When set to "disable", bypass-permissions mode is turned off for the + /// session regardless of other layers. Serialized as + /// disableBypassPermissionsMode. + /// + [JsonPropertyName("disableBypassPermissionsMode")] + public DisableBypassPermissionsMode? DisableBypassPermissionsMode { get; set; } + + /// Tool-permission patterns that are always denied. + [JsonPropertyName("deny")] + public IList? Deny { get; set; } + + /// Tool-permission patterns that require an explicit ask. + [JsonPropertyName("ask")] + public IList? Ask { get; set; } + + /// Tool-permission patterns that are allowed without prompting. + [JsonPropertyName("allow")] + public IList? Allow { get; set; } +} + +/// +/// Managed-settings layer injected at session startup. Currently carries only a +/// object. +/// +/// +/// This layer is startup-only and is not persisted with the session. It must be +/// re-supplied on to remain in +/// effect; omitting it on resume clears the previously injected layer. It can be +/// combined with . Older +/// runtimes may ignore this additive field, so hosts must not rely on injected +/// policy until they ship a compatible runtime. +/// +public sealed class ManagedSettings +{ + /// Permission rules for this managed-settings layer. + [JsonPropertyName("permissions")] + public ManagedSettingsPermissions? Permissions { get; set; } +} + /// /// Shared configuration properties for creating or resuming a Copilot session. /// Use when creating a new session, or @@ -3136,6 +3200,7 @@ protected SessionConfigBase(SessionConfigBase? other) RemoteSession = other.RemoteSession; ExpAssignments = other.ExpAssignments; EnableManagedSettings = other.EnableManagedSettings; + ManagedSettings = other.ManagedSettings; #pragma warning disable GHCP001 Canvases = other.Canvases is not null ? [.. other.Canvases] : null; RequestCanvasRenderer = other.RequestCanvasRenderer; @@ -3601,6 +3666,17 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableManagedSettings { get; set; } + /// + /// Optional managed-settings layer injected at session bootstrap. Currently + /// carries a permissions object that composes restrictively with any + /// server- or device-level managed settings. This layer is startup-only and + /// is not persisted: it must be re-supplied on resume to remain in effect, + /// and omitting it on resume clears the previously injected layer. Can be + /// combined with . Serialized on the wire + /// as managedSettings. + /// + public ManagedSettings? ManagedSettings { get; set; } + #pragma warning disable GHCP001 /// /// Canvas declarations advertised by this connection. The runtime forwards diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index 7b7d8c109..d4b4100b4 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -10,6 +10,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; +using GitHub.Copilot.Rpc; using Xunit; namespace GitHub.Copilot.Test.Unit; @@ -515,6 +516,94 @@ private static int GetPrivateDictionaryCount(CopilotClient client, string fieldN return (int)count.GetValue(dictionary)!; } + [Fact] + public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + var permissionInvocation = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + DisableBypassPermissionsMode = DisableBypassPermissionsMode.Disable, + Deny = ["shell(rm*)"], + Ask = ["write"], + Allow = [] + } + }, + OnPermissionRequest = (_, invocation) => + { + permissionInvocation.TrySetResult(invocation); + return Task.FromResult(PermissionDecision.NoResult()); + } + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(request.Params.TryGetProperty("enableManagedSettings", out _)); + var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); + Assert.Equal("disable", permissions.GetProperty("disableBypassPermissionsMode").GetString()); + Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); + Assert.Equal("write", Assert.Single(permissions.GetProperty("ask").EnumerateArray()).GetString()); + Assert.Empty(permissions.GetProperty("allow").EnumerateArray()); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "managed-permission" + } + }); + var invocation = await permissionInvocation.Task.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.True(invocation.ManagedSettingsEnabled); + } + + [Fact] + public async Task CreateSessionAsync_Omits_ManagedSettings_When_Unset() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(request.Params.TryGetProperty("managedSettings", out _)); + } + + [Fact] + public async Task ResumeSessionAsync_Serializes_ManagedSettings_Permissions() + { + 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.ResumeSessionAsync("session-managed", new ResumeSessionConfig + { + ManagedSettings = new ManagedSettings + { + Permissions = new ManagedSettingsPermissions + { + Deny = ["shell(rm*)"] + } + }, + OnPermissionRequest = PermissionHandler.ApproveAll, + OnEvent = _ => { } + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.resume"); + var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); + Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); + } + private static void DispatchEvent(CopilotSession session, SessionEvent evt) { var method = typeof(CopilotSession).GetMethod("DispatchEvent", BindingFlags.Instance | BindingFlags.NonPublic) diff --git a/dotnet/test/Unit/SessionEventSerializationTests.cs b/dotnet/test/Unit/SessionEventSerializationTests.cs index 64e28a5ae..326ac3f3c 100644 --- a/dotnet/test/Unit/SessionEventSerializationTests.cs +++ b/dotnet/test/Unit/SessionEventSerializationTests.cs @@ -368,4 +368,65 @@ public void McpOauthRequiredData_Preserves_Static_Client_Secret() Assert.NotNull(authEvent.Data.StaticClientConfig); Assert.Equal("static-secret", authEvent.Data.StaticClientConfig.ClientSecret); } + + [Fact] + public void ManagedSettingsResolvedData_Preserves_Client_Provenance() + { + Assert.Equal("server", ManagedSettingsResolvedSource.Server.Value); + Assert.Equal("device", ManagedSettingsResolvedSource.Device.Value); + Assert.Equal("client", ManagedSettingsResolvedSource.Client.Value); + Assert.Equal("mixed", ManagedSettingsResolvedSource.Mixed.Value); + Assert.Equal("none", ManagedSettingsResolvedSource.None.Value); + + const string clientJson = """ + { + "id": "11111111-1111-1111-1111-111111111111", + "timestamp": "2026-03-15T21:26:54.987Z", + "parentId": null, + "type": "session.managed_settings_resolved", + "data": { + "source": "client", + "serverManaged": false, + "deviceManaged": false, + "clientManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var clientEvent = Assert.IsType( + SessionEvent.FromJson(clientJson)); + Assert.Equal(ManagedSettingsResolvedSource.Client, clientEvent.Data.Source); + Assert.True(clientEvent.Data.ClientManaged); + using (var document = JsonDocument.Parse(clientEvent.ToJson())) + { + Assert.True(document.RootElement.GetProperty("data").GetProperty("clientManaged").GetBoolean()); + } + + const string mixedJson = """ + { + "id": "22222222-2222-2222-2222-222222222222", + "timestamp": "2026-03-15T21:26:54.987Z", + "parentId": null, + "type": "session.managed_settings_resolved", + "data": { + "source": "mixed", + "serverManaged": true, + "deviceManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var mixedEvent = Assert.IsType( + SessionEvent.FromJson(mixedJson)); + Assert.Equal(ManagedSettingsResolvedSource.Mixed, mixedEvent.Data.Source); + Assert.Null(mixedEvent.Data.ClientManaged); + using var mixedDocument = JsonDocument.Parse(mixedEvent.ToJson()); + Assert.False(mixedDocument.RootElement.GetProperty("data").TryGetProperty("clientManaged", out _)); + } } diff --git a/go/client.go b/go/client.go index d2c43c26b..856e933ea 100644 --- a/go/client.go +++ b/go/client.go @@ -750,6 +750,10 @@ func extractTransformCallbacks(config *SystemMessageConfig) (*SystemMessageConfi return wireConfig, callbacks } +func hasManagedSettings(enableManagedSettings *bool, managedSettings *ManagedSettings) bool { + return (enableManagedSettings != nil && *enableManagedSettings) || managedSettings != nil +} + func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Session, error) { if config == nil { config = &SessionConfig{} @@ -833,6 +837,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.ExtensionInfo = config.ExtensionInfo req.ExpAssignments = config.ExpAssignments req.EnableManagedSettings = config.EnableManagedSettings + req.ManagedSettings = config.ManagedSettings if len(config.Commands) > 0 { cmds := make([]wireCommand, 0, len(config.Commands)) @@ -917,7 +922,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses sessionID, c.client, "", - config.EnableManagedSettings != nil && *config.EnableManagedSettings, + hasManagedSettings(config.EnableManagedSettings, config.ManagedSettings), ) s.registerTools(config.Tools) @@ -1215,6 +1220,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.ExtensionInfo = config.ExtensionInfo req.ExpAssignments = config.ExpAssignments req.EnableManagedSettings = config.EnableManagedSettings + req.ManagedSettings = config.ManagedSettings if config.OnPermissionRequest != nil { req.RequestPermission = Bool(true) } @@ -1250,7 +1256,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, sessionID, c.client, "", - config.EnableManagedSettings != nil && *config.EnableManagedSettings, + hasManagedSettings(config.EnableManagedSettings, config.ManagedSettings), ) session.registerTools(config.Tools) diff --git a/go/client_test.go b/go/client_test.go index b5274cfda..3322d7741 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3563,3 +3563,162 @@ func TestIsTerminal(t *testing.T) { } }) } + +func TestSessionRequests_ManagedSettings(t *testing.T) { + settings := &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, + Deny: []string{"Shell(git push)"}, + Ask: []string{"Domain(publish.example)"}, + Allow: []string{"Read(**)"}, + }, + } + + expectedPermissions := map[string]any{ + "disableBypassPermissionsMode": "disable", + "deny": []any{"Shell(git push)"}, + "ask": []any{"Domain(publish.example)"}, + "allow": []any{"Read(**)"}, + } + + t.Run("direct injection enables managed safeguards", func(t *testing.T) { + if !hasManagedSettings(nil, settings) { + t.Fatal("expected injected managed settings to enable managed safeguards") + } + if hasManagedSettings(nil, nil) { + t.Fatal("expected an ordinary session to remain unmanaged") + } + }) + + t.Run("includes managedSettings on create when set", func(t *testing.T) { + req := createSessionRequest{EnableManagedSettings: Bool(true), ManagedSettings: settings} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if m["enableManagedSettings"] != true { + t.Errorf("Expected enableManagedSettings true, got %v", m["enableManagedSettings"]) + } + ms, ok := m["managedSettings"].(map[string]any) + if !ok { + t.Fatalf("Expected managedSettings object, got %v", m["managedSettings"]) + } + perms, ok := ms["permissions"].(map[string]any) + if !ok { + t.Fatalf("Expected permissions object, got %v", ms["permissions"]) + } + if !reflect.DeepEqual(perms, expectedPermissions) { + t.Errorf("permissions mismatch:\n got: %#v\nwant: %#v", perms, expectedPermissions) + } + }) + + t.Run("includes managedSettings on resume when set", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1", ManagedSettings: settings} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + if err := json.Unmarshal(data, &m); err != nil { + t.Fatalf("Failed to unmarshal: %v", err) + } + if _, ok := m["managedSettings"].(map[string]any); !ok { + t.Fatalf("Expected managedSettings object, got %v", m["managedSettings"]) + } + }) + + t.Run("omits managedSettings when nil", func(t *testing.T) { + req := createSessionRequest{} + data, _ := json.Marshal(req) + var m map[string]any + json.Unmarshal(data, &m) + if _, ok := m["managedSettings"]; ok { + t.Error("Expected managedSettings to be omitted when nil") + } + }) + + t.Run("preserves explicit empty permission arrays", func(t *testing.T) { + // A non-nil empty allow list is restrictive: it admits no operations. + // Preserve field presence while still omitting nil slices. + req := createSessionRequest{ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, + Deny: []string{}, + Ask: []string{}, + Allow: []string{}, + }, + }} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + json.Unmarshal(data, &m) + perms := m["managedSettings"].(map[string]any)["permissions"].(map[string]any) + if perms["disableBypassPermissionsMode"] != "disable" { + t.Errorf("Expected disableBypassPermissionsMode preserved, got %v", perms["disableBypassPermissionsMode"]) + } + for _, key := range []string{"deny", "ask", "allow"} { + if value, ok := perms[key].([]any); !ok || len(value) != 0 { + t.Errorf("Expected %s to be an explicit empty array, got %v", key, perms[key]) + } + } + }) + + t.Run("distinguishes explicit empty allow from an absent allow", func(t *testing.T) { + // Security-critical: a present empty allow list admits nothing, while an + // absent allow list imposes no allow restriction. The wire output must + // tell these apart per-field, so an explicit empty slice serializes as + // `[]` while a nil slice is omitted entirely. + req := createSessionRequest{ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + Allow: []string{}, // present but empty: admit nothing + // Deny and Ask left nil: no such restriction supplied. + }, + }} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + json.Unmarshal(data, &m) + perms := m["managedSettings"].(map[string]any)["permissions"].(map[string]any) + + allow, ok := perms["allow"].([]any) + if !ok || len(allow) != 0 { + t.Errorf("Expected allow to be an explicit empty array, got %v", perms["allow"]) + } + if _, present := perms["deny"]; present { + t.Errorf("Expected deny to be omitted when nil, got %v", perms["deny"]) + } + if _, present := perms["ask"]; present { + t.Errorf("Expected ask to be omitted when nil, got %v", perms["ask"]) + } + }) + + t.Run("distinguishes explicit empty arrays on resume", func(t *testing.T) { + req := resumeSessionRequest{SessionID: "s1", ManagedSettings: &ManagedSettings{ + Permissions: &ManagedSettingsPermissions{ + Deny: []string{}, + Ask: []string{}, + Allow: []string{}, + }, + }} + data, err := json.Marshal(req) + if err != nil { + t.Fatalf("Failed to marshal: %v", err) + } + var m map[string]any + json.Unmarshal(data, &m) + perms := m["managedSettings"].(map[string]any)["permissions"].(map[string]any) + for _, key := range []string{"deny", "ask", "allow"} { + if value, ok := perms[key].([]any); !ok || len(value) != 0 { + t.Errorf("Expected %s to be an explicit empty array on resume, got %v", key, perms[key]) + } + } + }) +} diff --git a/go/session_event_serialization_test.go b/go/session_event_serialization_test.go index bd47fdfbe..ee9258b22 100644 --- a/go/session_event_serialization_test.go +++ b/go/session_event_serialization_test.go @@ -189,3 +189,69 @@ func TestRawSessionEventDataWithNilRawMarshalsAsNull(t *testing.T) { t.Fatalf("expected missing raw data to marshal as null, got %v", serialized["data"]) } } + +func TestManagedSettingsResolvedProvenanceRoundTrips(t *testing.T) { + sources := []ManagedSettingsResolvedSource{ + ManagedSettingsResolvedSourceServer, + ManagedSettingsResolvedSourceDevice, + ManagedSettingsResolvedSourceClient, + ManagedSettingsResolvedSourceMixed, + ManagedSettingsResolvedSourceNone, + } + expectedSources := []string{"server", "device", "client", "mixed", "none"} + for i, source := range sources { + if string(source) != expectedSources[i] { + t.Fatalf("expected source %q, got %q", expectedSources[i], source) + } + } + + clientManaged := true + resolved := SessionManagedSettingsResolvedData{ + BypassPermissionsDisabled: true, + ClientManaged: &clientManaged, + DeviceManaged: false, + FailClosed: false, + ManagedKeys: []string{"permissions"}, + ServerManaged: false, + Source: ManagedSettingsResolvedSourceClient, + } + data, err := json.Marshal(resolved) + if err != nil { + t.Fatalf("failed to marshal managed settings resolution: %v", err) + } + + var serialized map[string]any + if err := json.Unmarshal(data, &serialized); err != nil { + t.Fatalf("failed to inspect managed settings resolution: %v", err) + } + if serialized["source"] != "client" || serialized["clientManaged"] != true { + t.Fatalf("expected client provenance, got %v", serialized) + } + + var roundTripped SessionManagedSettingsResolvedData + if err := json.Unmarshal(data, &roundTripped); err != nil { + t.Fatalf("failed to round-trip managed settings resolution: %v", err) + } + if roundTripped.Source != ManagedSettingsResolvedSourceClient || + roundTripped.ClientManaged == nil || + !*roundTripped.ClientManaged { + t.Fatalf("expected client provenance to round-trip, got %#v", roundTripped) + } + + resolved.Source = ManagedSettingsResolvedSourceMixed + resolved.ClientManaged = nil + data, err = json.Marshal(resolved) + if err != nil { + t.Fatalf("failed to marshal mixed managed settings resolution: %v", err) + } + serialized = nil + if err := json.Unmarshal(data, &serialized); err != nil { + t.Fatalf("failed to inspect mixed managed settings resolution: %v", err) + } + if serialized["source"] != "mixed" { + t.Fatalf("expected mixed provenance, got %v", serialized["source"]) + } + if _, ok := serialized["clientManaged"]; ok { + t.Fatalf("expected absent clientManaged to be omitted, got %v", serialized) + } +} diff --git a/go/types.go b/go/types.go index 6669564bf..6d6a877d3 100644 --- a/go/types.go +++ b/go/types.go @@ -1498,6 +1498,51 @@ type SessionConfig struct { // be set; if omitted, the runtime is expected to reject session creation // (fail-closed). Unset behaves exactly as before. EnableManagedSettings *bool + // ManagedSettings supplies host-injected enterprise managed settings for + // the session. Unlike EnableManagedSettings (which asks the runtime to + // self-fetch account/org and device policy), this provides the managed + // policy directly. The runtime validates it with the same + // managed-permission parser it uses for fetched policy and composes it + // restrictively with any self-fetched (server) and device-managed (MDM) + // layers. It is startup-only and not persisted: re-supply it on resume, + // where it replaces the prior injected layer (omitting it clears the + // layer). It may be combined with EnableManagedSettings. Requires a runtime + // whose RPC schema includes managedSettings. + ManagedSettings *ManagedSettings +} + +// ManagedSettings is host-injected enterprise managed settings for a session. +// The first supported contract is permissions-only; unknown sibling keys are +// rejected by the runtime. Serialized on the wire as managedSettings. +type ManagedSettings struct { + // Permissions is the managed permission policy for the session. + Permissions *ManagedSettingsPermissions `json:"permissions,omitempty"` +} + +// DisableBypassPermissionsMode is the managed bypass-permissions policy. +type DisableBypassPermissionsMode = rpc.DisableBypassPermissionsMode + +const ( + // DisableBypassPermissionsModeDisable turns off bypass-permissions mode. + DisableBypassPermissionsModeDisable = rpc.DisableBypassPermissionsModeDisable +) + +// ManagedSettingsPermissions is the permissions-only managed policy injected +// via ManagedSettings. Rule strings use the same vocabulary the runtime +// accepts for fetched managed policy (e.g. "Read(**)", "Shell(git push *)"); +// malformed rules are rejected by the runtime at session creation. +type ManagedSettingsPermissions struct { + // DisableBypassPermissionsMode, when set to "disable", turns off + // bypass-permissions ("yolo") mode for the session. Deny-wins: no other + // layer can re-enable it. + DisableBypassPermissionsMode DisableBypassPermissionsMode `json:"disableBypassPermissionsMode,omitempty"` + // Deny lists operations that must always be denied. Unioned across layers. + Deny []string `json:"deny,omitzero"` + // Ask lists operations that must prompt for approval. Unioned across layers. + Ask []string `json:"ask,omitzero"` + // Allow lists operations permitted without prompting. Every declared allow + // list across managed layers must admit an operation for it to be allowed. + Allow []string `json:"allow,omitzero"` } // ToolDefer controls whether a tool may be deferred (loaded lazily via tool @@ -1961,6 +2006,11 @@ type ResumeSessionConfig struct { // SessionConfig.EnableManagedSettings. Re-supply on resume so the runtime // re-applies the managed-settings self-fetch after a CLI process restart. EnableManagedSettings *bool + // ManagedSettings re-injects host-provided managed settings on resume. See + // SessionConfig.ManagedSettings. It must be re-supplied on resume: it + // replaces the prior injected layer, and omitting it clears that layer so + // warm and cold resume behave identically. + ManagedSettings *ManagedSettings } // ProviderTokenArgs carries the context passed to a [BearerTokenProvider] callback @@ -2423,6 +2473,7 @@ type createSessionRequest struct { CanvasProvider *CanvasProviderIdentity `json:"canvasProvider,omitempty"` ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` } @@ -2519,6 +2570,7 @@ type resumeSessionRequest struct { CanvasProvider *CanvasProviderIdentity `json:"canvasProvider,omitempty"` ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` } diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index add4a79b6..23e4f77b4 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -201,6 +201,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setCloud(config.getCloud()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + request.setManagedSettings(config.getManagedSettings()); return request; } @@ -337,6 +338,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setRemoteSession(config.getRemoteSession()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + request.setManagedSettings(config.getManagedSettings()); return request; } @@ -374,7 +376,8 @@ static void configureSession(CopilotSession session, SessionConfig config) { if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } - session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); + session.setManagedSettingsEnabled( + config.getEnableManagedSettings().orElse(false) || config.getManagedSettings() != null); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } @@ -425,7 +428,8 @@ static void configureSession(CopilotSession session, ResumeSessionConfig config) if (config.getOnPermissionRequest() != null) { session.registerPermissionHandler(config.getOnPermissionRequest()); } - session.setManagedSettingsEnabled(config.getEnableManagedSettings().orElse(false)); + session.setManagedSettingsEnabled( + config.getEnableManagedSettings().orElse(false) || config.getManagedSettings() != null); if (config.getOnMcpAuthRequest() != null) { session.registerMcpAuthHandler(config.getOnMcpAuthRequest()); } diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 46f59a28c..4c74e38ac 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -234,6 +234,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("managedSettings") + @JsonInclude(JsonInclude.Include.NON_NULL) + private ManagedSettings managedSettings; + /** Gets the model name. @return the model */ public String getModel() { return model; @@ -1095,4 +1099,17 @@ public void setEnableManagedSettings(boolean enableManagedSettings) { public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * @param managedSettings + * host-injected managed settings + */ + public void setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + } } diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java b/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java new file mode 100644 index 000000000..39e8fcf55 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Managed settings an SDK host may inject at session create or resume. + * + *

+ * The initial public contract is permissions-only. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class ManagedSettings { + @JsonProperty("permissions") + private ManagedSettingsPermissions permissions; + + /** @return the managed permission policy, or {@code null} when unset */ + public ManagedSettingsPermissions getPermissions() { + return permissions; + } + + /** + * @param permissions + * managed permission policy + * @return this settings object + */ + public ManagedSettings setPermissions(ManagedSettingsPermissions permissions) { + this.permissions = permissions; + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java b/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java new file mode 100644 index 000000000..0923cea54 --- /dev/null +++ b/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.generated.rpc.DisableBypassPermissionsMode; +import java.util.ArrayList; +import java.util.List; + +/** + * Enterprise permission policy injected by an SDK host at session startup. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class ManagedSettingsPermissions { + @JsonProperty("disableBypassPermissionsMode") + private DisableBypassPermissionsMode disableBypassPermissionsMode; + + @JsonProperty("deny") + private List deny; + + @JsonProperty("ask") + private List ask; + + @JsonProperty("allow") + private List allow; + + /** @return the bypass-permissions policy, or {@code null} when unset */ + public DisableBypassPermissionsMode getDisableBypassPermissionsMode() { + return disableBypassPermissionsMode; + } + + /** + * Disables bypass/allow-all permission modes. + * + * @param value + * bypass-permissions policy + * @return this policy + */ + public ManagedSettingsPermissions setDisableBypassPermissionsMode(DisableBypassPermissionsMode value) { + this.disableBypassPermissionsMode = value; + return this; + } + + /** @return rules that deny matching operations, or {@code null} when unset */ + public List getDeny() { + return deny; + } + + /** + * @param rules + * deny rules + * @return this policy + */ + public ManagedSettingsPermissions setDeny(List rules) { + this.deny = rules == null ? null : new ArrayList<>(rules); + return this; + } + + /** @return rules that require approval, or {@code null} when unset */ + public List getAsk() { + return ask; + } + + /** + * @param rules + * ask rules + * @return this policy + */ + public ManagedSettingsPermissions setAsk(List rules) { + this.ask = rules == null ? null : new ArrayList<>(rules); + return this; + } + + /** @return rules that allow matching operations, or {@code null} when unset */ + public List getAllow() { + return allow; + } + + /** + * @param rules + * allow rules + * @return this policy + */ + public ManagedSettingsPermissions setAllow(List rules) { + this.allow = rules == null ? null : new ArrayList<>(rules); + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 1e32ec847..10641157b 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -107,6 +107,7 @@ public class ResumeSessionConfig { private String remoteSession; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the AI model to use. @@ -1910,6 +1911,24 @@ public ResumeSessionConfig setEnableManagedSettings(boolean enableManagedSetting return this; } + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * Supplies permissions-only managed settings for this resume. The value + * replaces the prior injected layer and is not persisted. + * + * @param managedSettings + * the host-injected managed settings + * @return this config for method chaining + */ + public ResumeSessionConfig setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + return this; + } + /** * Creates a shallow clone of this {@code ResumeSessionConfig} instance. *

@@ -1992,6 +2011,7 @@ public ResumeSessionConfig clone() { copy.remoteSession = this.remoteSession; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.managedSettings = this.managedSettings; return copy; } } diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 3fe17b182..8c9d03ede 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -236,6 +236,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("managedSettings") + @JsonInclude(JsonInclude.Include.NON_NULL) + private ManagedSettings managedSettings; + /** Gets the session ID. @return the session ID */ public String getSessionId() { return sessionId; @@ -1110,4 +1114,17 @@ public void setEnableManagedSettings(boolean enableManagedSettings) { public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + + /** @return host-injected managed settings, or {@code null} when unset */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * @param managedSettings + * host-injected managed settings + */ + public void setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + } } diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index ad62551fd..3ccda690f 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -108,6 +108,7 @@ public class SessionConfig { private CloudSessionOptions cloud; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private ManagedSettings managedSettings; /** * Gets the custom session ID. @@ -2041,6 +2042,29 @@ public SessionConfig setEnableManagedSettings(boolean enableManagedSettings) { return this; } + /** + * Gets host-injected managed settings for this session. + * + * @return the managed settings, or {@code null} when unset + */ + public ManagedSettings getManagedSettings() { + return managedSettings; + } + + /** + * Supplies permissions-only managed settings at session startup. The runtime + * validates and composes this policy restrictively with self-fetched and device + * policy. Re-supply it on resume because it is not persisted. + * + * @param managedSettings + * the host-injected managed settings + * @return this config instance for method chaining + */ + public SessionConfig setManagedSettings(ManagedSettings managedSettings) { + this.managedSettings = managedSettings; + return this; + } + /** * Creates a shallow clone of this {@code SessionConfig} instance. *

@@ -2128,6 +2152,7 @@ public SessionConfig clone() { copy.cloud = this.cloud; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.managedSettings = this.managedSettings; return copy; } } diff --git a/java/src/test/java/com/github/copilot/ManagedSettingsTest.java b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java new file mode 100644 index 000000000..dbd19f3c9 --- /dev/null +++ b/java/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.generated.rpc.DisableBypassPermissionsMode; +import com.github.copilot.rpc.ManagedSettings; +import com.github.copilot.rpc.ManagedSettingsPermissions; +import com.github.copilot.rpc.PermissionRequestResult; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.jupiter.api.Test; + +class ManagedSettingsTest { + @Test + void forwardsManagedSettingsOnCreateAndResume() throws Exception { + var permissions = new ManagedSettingsPermissions() + .setDisableBypassPermissionsMode(DisableBypassPermissionsMode.DISABLE).setDeny(List.of("Shell(rm *)")) + .setAsk(List.of("Domain(publish.example)")).setAllow(List.of("Read(**)")); + var managedSettings = new ManagedSettings().setPermissions(permissions); + + var create = SessionRequestBuilder.buildCreateRequest( + new SessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings), + "managed-create"); + var resume = SessionRequestBuilder.buildResumeRequest("managed-resume", + new ResumeSessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings)); + + assertEquals(managedSettings, create.getManagedSettings()); + assertEquals(managedSettings, resume.getManagedSettings()); + var json = new ObjectMapper().writeValueAsString(create); + assertTrue(json.contains("\"enableManagedSettings\":true")); + assertTrue(json.contains("\"managedSettings\":{\"permissions\"")); + assertTrue(json.contains("\"disableBypassPermissionsMode\":\"disable\"")); + } + + @Test + void preservesExplicitEmptyPermissionArrays() throws Exception { + // Security-critical: a present empty allow list admits nothing, while an + // absent (null) list imposes no such restriction. Jackson NON_NULL must + // emit an explicit empty array as `[]` and omit null fields, so the two + // remain distinguishable on the wire. + var permissions = new ManagedSettingsPermissions().setDeny(List.of()).setAsk(List.of()).setAllow(List.of()); + var managedSettings = new ManagedSettings().setPermissions(permissions); + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setManagedSettings(managedSettings), + "managed-empty"); + + var json = new ObjectMapper().writeValueAsString(create); + assertTrue(json.contains("\"deny\":[]"), json); + assertTrue(json.contains("\"ask\":[]"), json); + assertTrue(json.contains("\"allow\":[]"), json); + } + + @Test + void distinguishesExplicitEmptyAllowFromAbsentAllow() throws Exception { + // Present empty allow admits nothing; the null deny/ask must be omitted. + var permissions = new ManagedSettingsPermissions().setAllow(List.of()); + var managedSettings = new ManagedSettings().setPermissions(permissions); + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setManagedSettings(managedSettings), + "managed-mixed"); + + var json = new ObjectMapper().writeValueAsString(create); + assertTrue(json.contains("\"allow\":[]"), json); + assertFalse(json.contains("\"deny\""), json); + assertFalse(json.contains("\"ask\""), json); + } + + @Test + void directInjectionEnablesManagedSafeguards() throws Exception { + var session = new CopilotSession("session-1", null); + var settings = new ManagedSettings().setPermissions(new ManagedSettingsPermissions()); + var managedSettingsEnabled = new AtomicBoolean(); + var config = new SessionConfig().setManagedSettings(settings).setOnPermissionRequest((request, invocation) -> { + managedSettingsEnabled.set(invocation.isManagedSettingsEnabled()); + return CompletableFuture.completedFuture(PermissionRequestResult.noResult()); + }); + + SessionRequestBuilder.configureSession(session, config); + session.handlePermissionRequest(new ObjectMapper().readTree("{\"kind\":\"read\"}")).get(); + + assertTrue(managedSettingsEnabled.get()); + } +} diff --git a/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java b/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java index fc978ed65..8d9b70a34 100644 --- a/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java +++ b/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java @@ -113,6 +113,54 @@ void testParseSessionIdleEvent() throws Exception { assertEquals("session.idle", event.getType()); } + @Test + void testManagedSettingsResolvedClientProvenance() throws Exception { + assertEquals("server", ManagedSettingsResolvedSource.SERVER.getValue()); + assertEquals("device", ManagedSettingsResolvedSource.DEVICE.getValue()); + assertEquals("client", ManagedSettingsResolvedSource.CLIENT.getValue()); + assertEquals("mixed", ManagedSettingsResolvedSource.MIXED.getValue()); + assertEquals("none", ManagedSettingsResolvedSource.NONE.getValue()); + + String clientJson = """ + { + "type": "session.managed_settings_resolved", + "data": { + "source": "client", + "serverManaged": false, + "deviceManaged": false, + "clientManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var clientEvent = assertInstanceOf(SessionManagedSettingsResolvedEvent.class, parseJson(clientJson)); + assertEquals(ManagedSettingsResolvedSource.CLIENT, clientEvent.getData().source()); + assertEquals(Boolean.TRUE, clientEvent.getData().clientManaged()); + assertTrue(MAPPER.writeValueAsString(clientEvent).contains("\"clientManaged\":true")); + + String mixedJson = """ + { + "type": "session.managed_settings_resolved", + "data": { + "source": "mixed", + "serverManaged": true, + "deviceManaged": true, + "failClosed": false, + "bypassPermissionsDisabled": true, + "managedKeys": ["permissions"] + } + } + """; + + var mixedEvent = assertInstanceOf(SessionManagedSettingsResolvedEvent.class, parseJson(mixedJson)); + assertEquals(ManagedSettingsResolvedSource.MIXED, mixedEvent.getData().source()); + assertNull(mixedEvent.getData().clientManaged()); + assertFalse(MAPPER.writeValueAsString(mixedEvent).contains("\"clientManaged\"")); + } + @Test void testParseSessionInfoEvent() throws Exception { String json = """ @@ -897,15 +945,16 @@ void testParseEmptyJson() throws Exception { @Test void testParseAllEventTypes() throws Exception { String[] types = {"session.start", "session.resume", "session.error", "session.idle", "session.info", - "session.model_change", "session.mode_changed", "session.plan_changed", - "session.workspace_file_changed", "session.handoff", "session.truncation", "session.snapshot_rewind", - "session.usage_info", "session.compaction_start", "session.compaction_complete", "user.message", - "pending_messages.modified", "assistant.turn_start", "assistant.intent", "assistant.reasoning", - "assistant.reasoning_delta", "assistant.message", "assistant.message_delta", "assistant.turn_end", - "assistant.usage", "abort", "tool.user_requested", "tool.execution_start", - "tool.execution_partial_result", "tool.execution_progress", "tool.execution_complete", - "subagent.started", "subagent.completed", "subagent.failed", "subagent.selected", "hook.start", - "hook.end", "system.message", "session.shutdown", "skill.invoked"}; + "session.model_change", "session.mode_changed", "session.managed_settings_resolved", + "session.managed_settings_enforced", "session.plan_changed", "session.workspace_file_changed", + "session.handoff", "session.truncation", "session.snapshot_rewind", "session.usage_info", + "session.compaction_start", "session.compaction_complete", "user.message", "pending_messages.modified", + "assistant.turn_start", "assistant.intent", "assistant.reasoning", "assistant.reasoning_delta", + "assistant.message", "assistant.message_delta", "assistant.turn_end", "assistant.usage", "abort", + "tool.user_requested", "tool.execution_start", "tool.execution_partial_result", + "tool.execution_progress", "tool.execution_complete", "subagent.started", "subagent.completed", + "subagent.failed", "subagent.selected", "hook.start", "hook.end", "system.message", "session.shutdown", + "skill.invoked"}; for (String type : types) { String json = """ diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 4ed139be7..c30b2207b 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1467,7 +1467,9 @@ export class CopilotClient { this.onGetTraceContext, { mcpAuthHandler: config.onMcpAuthRequest, - managedSettingsEnabled: config.enableManagedSettings, + managedSettingsEnabled: + config.enableManagedSettings === true || + config.managedSettings !== undefined, } ); s.registerTools(config.tools); @@ -1608,6 +1610,7 @@ export class CopilotClient { cloud: config.cloud, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + managedSettings: config.managedSettings, }); const { @@ -1708,7 +1711,8 @@ export class CopilotClient { this.onGetTraceContext, { mcpAuthHandler: config.onMcpAuthRequest, - managedSettingsEnabled: config.enableManagedSettings, + managedSettingsEnabled: + config.enableManagedSettings === true || config.managedSettings !== undefined, } ); session.registerTools(config.tools); @@ -1855,6 +1859,7 @@ export class CopilotClient { openCanvases: config.openCanvases, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + managedSettings: config.managedSettings, }); const { workspacePath, capabilities, openCanvases } = response as { diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index f915a8707..5ab53471a 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -107,6 +107,8 @@ export type { DefaultAgentConfig, BearerTokenProvider, MessageOptions, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index d77a3a97c..4ff279189 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2088,6 +2088,45 @@ export interface GitHubMcpToolConfig { disableFormDeferral?: boolean; } +/** + * Permissions-only managed policy injected by the host via + * {@link SessionConfigBase.managedSettings}. + * + * Rule strings use the same vocabulary the runtime accepts for fetched managed + * policy (e.g. `"Read(**)"`, `"Shell(git push *)"`); malformed rules are + * rejected at session creation. + */ +export interface ManagedSettingsPermissions { + /** + * When set to `"disable"`, bypass-permissions ("yolo") mode is turned off + * for the session. This is deny-wins: it cannot be re-enabled by any other + * layer. + */ + disableBypassPermissionsMode?: "disable"; + /** Operations that must always be denied. Unioned across managed layers. */ + deny?: string[]; + /** + * Operations that must prompt for approval. Unioned across managed layers. + */ + ask?: string[]; + /** + * Operations permitted without prompting. Every declared `allow` list + * (across managed layers) must admit an operation for it to be allowed. + */ + allow?: string[]; +} + +/** + * Host-injected enterprise managed settings. The first supported contract is + * permissions-only; unknown sibling keys are rejected by the runtime. + * + * @see {@link SessionConfigBase.managedSettings} + */ +export interface ManagedSettings { + /** Managed permission policy for the session. */ + permissions?: ManagedSettingsPermissions; +} + /** * Shared configuration fields used by both {@link SessionConfig} (for * creating a new session) and {@link ResumeSessionConfig} (for resuming @@ -2586,6 +2625,31 @@ export interface SessionConfigBase { */ enableManagedSettings?: boolean; + /** + * Host-injected enterprise managed settings for this session. + * + * Unlike {@link SessionConfigBase.enableManagedSettings} — which asks the + * runtime to *self-fetch* account/org and device policy — this field lets + * the host supply the managed policy directly. The runtime validates it + * with the same managed-permission parser it uses for fetched policy and + * composes it restrictively with any self-fetched (server) and + * device-managed (MDM) layers: `deny`/`ask` rules are unioned, every + * declared `allow` list must admit an operation, and + * `disableBypassPermissionsMode: "disable"` is deny-wins. + * + * This is startup-only. It is **not** persisted: it must be re-supplied on + * {@link CopilotClient.resumeSession | resume}, where it replaces the prior + * injected layer (omitting it clears the layer, so warm and cold resume + * behave identically). It may be combined with `enableManagedSettings`; + * when both are supplied the injected, server, and device restrictions all + * apply. + * + * Requires a Copilot runtime whose RPC schema includes `managedSettings`. + * Older runtimes may ignore this additive field, so hosts must not rely on + * injected policy until they ship a compatible runtime. + */ + managedSettings?: ManagedSettings; + /** * When true, skips embedding-based retrieval for this session. * Use in multitenant deployments to prevent cross-session information leakage diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 962d90970..01a97e980 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3659,3 +3659,101 @@ describe("CopilotClient", () => { }); }); }); + +describe("managedSettings serialization", () => { + async function captureCreateParams(config: Record): Promise { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + await client.createSession({ onPermissionRequest: approveAll, ...config }); + const call = spy.mock.calls.find(([method]) => method === "session.create"); + return call![1]; + } + + it("forwards the full permissions object on session.create", async () => { + const params = await captureCreateParams({ + managedSettings: { + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["Shell(git push)"], + ask: ["Domain(publish.example)"], + allow: ["Read(**)"], + }, + }, + }); + expect(params.managedSettings).toEqual({ + permissions: { + disableBypassPermissionsMode: "disable", + deny: ["Shell(git push)"], + ask: ["Domain(publish.example)"], + allow: ["Read(**)"], + }, + }); + }); + + it("marks directly injected sessions as managed", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + vi.spyOn((client as any).connection!, "sendRequest").mockImplementation( + async (method: string, params: any) => { + if (method === "session.create") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + } + ); + + const session = await client.createSession({ + onPermissionRequest: approveAll, + managedSettings: { permissions: { deny: ["Edit(/secrets/**)"] } }, + }); + + expect((session as any).managedSettingsEnabled).toBe(true); + }); + + it("omits managedSettings when not supplied", async () => { + const params = await captureCreateParams({}); + expect(params.managedSettings).toBeUndefined(); + }); + + it("coexists with enableManagedSettings", async () => { + const params = await captureCreateParams({ + enableManagedSettings: true, + managedSettings: { permissions: { deny: ["Edit(/secrets/**)"] } }, + }); + expect(params.enableManagedSettings).toBe(true); + expect(params.managedSettings).toEqual({ permissions: { deny: ["Edit(/secrets/**)"] } }); + }); + + it("preserves empty arrays in the permissions object", async () => { + const params = await captureCreateParams({ + managedSettings: { permissions: { deny: [], ask: [], allow: [] } }, + }); + expect(params.managedSettings).toEqual({ permissions: { deny: [], ask: [], allow: [] } }); + }); + + it("forwards managedSettings on session.resume", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + const spy = vi + .spyOn((client as any).connection!, "sendRequest") + .mockImplementation(async (method: string, params: any) => { + if (method === "session.resume") return { sessionId: params.sessionId }; + throw new Error(`Unexpected method: ${method}`); + }); + await client.resumeSession("session-1", { + onPermissionRequest: approveAll, + managedSettings: { permissions: { ask: ["Domain(publish.example)"] } }, + }); + const call = spy.mock.calls.find(([method]) => method === "session.resume"); + expect(call![1].managedSettings).toEqual({ + permissions: { ask: ["Domain(publish.example)"] }, + }); + }); +}); diff --git a/nodejs/test/session-event-types.test.ts b/nodejs/test/session-event-types.test.ts index fef7acdb2..5a8f2ca52 100644 --- a/nodejs/test/session-event-types.test.ts +++ b/nodejs/test/session-event-types.test.ts @@ -22,6 +22,9 @@ import type { PermissionRequest, PermissionRequestedData, PermissionRequestedEvent, + ManagedSettingsResolvedData, + ManagedSettingsResolvedEvent, + ManagedSettingsResolvedSource, // *Data payload types from the v0.3.0 generated session-event schema. AssistantMessageData, @@ -163,6 +166,45 @@ describe("Session event type exports (#1156)", () => { expect(permissionEvent.data.permissionRequest.managedApprovalRequired).toBe(true); }); + it("exposes managed settings client and mixed provenance", () => { + const sources: ManagedSettingsResolvedSource[] = [ + "server", + "device", + "client", + "mixed", + "none", + ]; + expect(sources).toEqual(["server", "device", "client", "mixed", "none"]); + + const clientData: ManagedSettingsResolvedData = { + bypassPermissionsDisabled: true, + clientManaged: true, + deviceManaged: false, + failClosed: false, + managedKeys: ["permissions"], + serverManaged: false, + source: "client", + }; + const clientEvent: ManagedSettingsResolvedEvent = { + ephemeral: true, + id: "evt-managed-1", + parentId: null, + timestamp: "2026-01-01T00:00:00.000Z", + type: "session.managed_settings_resolved", + data: clientData, + }; + expect(clientEvent.data.source).toBe("client"); + expect(clientEvent.data.clientManaged).toBe(true); + + const { clientManaged: _, ...withoutClientManaged } = clientData; + const mixedData: ManagedSettingsResolvedData = { + ...withoutClientManaged, + source: "mixed", + }; + expect(mixedData.source).toBe("mixed"); + expect("clientManaged" in mixedData).toBe(false); + }); + it("rejects approveAll in managed settings sessions", () => { expect(() => approveAll( @@ -260,6 +302,7 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); assertImportable(); assertImportable(); @@ -270,6 +313,8 @@ describe("Session event type exports (#1156)", () => { assertImportable(); assertImportable(); assertImportable(); + assertImportable(); + assertImportable(); // Supporting auxiliary types referenced by the *Data shapes — these // must round-trip through the package root too, otherwise consumers diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 678fffbf1..a7366db54 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -41,6 +41,8 @@ GetStatusResponse, InProcessRuntimeConnection, LogLevel, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -272,6 +274,8 @@ "McpAuthStaticClientConfig", "McpAuthToken", "McpAuthWwwAuthenticateParams", + "ManagedSettings", + "ManagedSettingsPermissions", "ModelBilling", "ModelBillingTokenPrices", "ModelBillingTokenPricesLongContext", diff --git a/python/copilot/client.py b/python/copilot/client.py index 7c273bd01..21ceb6eee 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -247,6 +247,63 @@ def _capi_session_options_to_wire(options: CapiSessionOptions) -> dict[str, Any] return wire +@dataclass +class ManagedSettingsPermissions: + """Permissions-only managed policy injected via :class:`ManagedSettings`. + + Rule strings use the same vocabulary the runtime accepts for fetched + managed policy (e.g. ``"Read(**)"``, ``"Shell(git push *)"``); malformed + rules are rejected by the runtime at session creation. + """ + + disable_bypass_permissions_mode: Literal["disable"] | None = None + """When ``"disable"``, turns off bypass-permissions ("yolo") mode for the + session. Deny-wins: no other layer can re-enable it. Sent on the wire as + ``disableBypassPermissionsMode``.""" + deny: list[str] | None = None + """Operations that must always be denied. Unioned across managed layers.""" + ask: list[str] | None = None + """Operations that must prompt for approval. Unioned across managed layers.""" + allow: list[str] | None = None + """Operations permitted without prompting. Every declared ``allow`` list + across managed layers must admit an operation for it to be allowed.""" + + +@dataclass +class ManagedSettings: + """Host-injected enterprise managed settings for a session. + + Unlike ``enable_managed_settings`` — which asks the runtime to *self-fetch* + account/org and device policy — this supplies the managed policy directly. + The runtime validates it with the same managed-permission parser it uses + for fetched policy and composes it restrictively with any self-fetched + (server) and device-managed (MDM) layers. + + The first supported contract is permissions-only; unknown sibling keys are + rejected by the runtime. Serialized on the wire as ``managedSettings``. + """ + + permissions: ManagedSettingsPermissions | None = None + """Managed permission policy for the session.""" + + +def _managed_settings_to_dict(settings: ManagedSettings) -> dict[str, Any]: + wire: dict[str, Any] = {} + permissions = settings.permissions + if permissions is not None: + perms: dict[str, Any] = {} + if permissions.disable_bypass_permissions_mode is not None: + perms["disableBypassPermissionsMode"] = permissions.disable_bypass_permissions_mode + if permissions.deny is not None: + perms["deny"] = list(permissions.deny) + if permissions.ask is not None: + perms["ask"] = list(permissions.ask) + if permissions.allow is not None: + perms["allow"] = list(permissions.allow) + wire["permissions"] = perms + return wire + + # Implicit provider name for the singular, whole-session ``provider`` config. # Named providers are keyed by their own ``name``. _DEFAULT_BEARER_TOKEN_PROVIDER_NAME = "default" @@ -2090,6 +2147,7 @@ async def create_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, + managed_settings: ManagedSettings | None = None, ) -> CopilotSession: """ Create a new conversation session with the Copilot CLI. @@ -2241,6 +2299,15 @@ async def create_session( expected to reject session creation (fail-closed). When unset, behaves exactly as before. Sent on the wire as ``enableManagedSettings``. + managed_settings: Host-injected enterprise managed settings for the + session. Supplies managed policy directly instead of + self-fetching; the runtime validates it and composes it + restrictively with any self-fetched (server) and device-managed + layers. Startup-only and not persisted: re-supply on + :meth:`resume_session` (omitting it clears the injected layer). + May be combined with ``enable_managed_settings``. Requires a + runtime whose RPC schema includes ``managedSettings``. Sent on + the wire as ``managedSettings``. Returns: A :class:`CopilotSession` instance for the new session. @@ -2389,6 +2456,10 @@ async def create_session( if enable_managed_settings is not None: payload["enableManagedSettings"] = enable_managed_settings + # Host-injected managed settings (permissions-only contract) + if managed_settings is not None: + payload["managedSettings"] = _managed_settings_to_dict(managed_settings) + # Add working directory if provided if working_directory: payload["workingDirectory"] = working_directory @@ -2575,7 +2646,8 @@ def _initialize_session(sid: str) -> CopilotSession: sid, self._client, workspace_path=None, - managed_settings_enabled=enable_managed_settings is True, + managed_settings_enabled=enable_managed_settings is True + or managed_settings is not None, ) if self._session_fs_config: if create_session_fs_handler is None: @@ -2799,6 +2871,7 @@ async def resume_session( exp_assignments: CopilotExpAssignmentResponse | None = None, enable_managed_settings: bool | None = None, github_mcp_tool_config: GitHubMcpToolConfig | None = None, + managed_settings: ManagedSettings | None = None, ) -> CopilotSession: """ Resume an existing conversation session by its ID. @@ -2951,6 +3024,11 @@ async def resume_session( expected to reject session creation (fail-closed). When unset, behaves exactly as before. Sent on the wire as ``enableManagedSettings``. + managed_settings: Host-injected enterprise managed settings for the + session. Must be re-supplied on resume; it replaces the prior + injected layer, and omitting it clears that layer so warm and + cold resume behave identically. See :meth:`create_session`. Sent + on the wire as ``managedSettings``. Returns: A :class:`CopilotSession` instance for the resumed session. @@ -3122,6 +3200,10 @@ async def resume_session( if enable_managed_settings is not None: payload["enableManagedSettings"] = enable_managed_settings + # Host-injected managed settings (permissions-only contract) + if managed_settings is not None: + payload["managedSettings"] = _managed_settings_to_dict(managed_settings) + if working_directory: payload["workingDirectory"] = working_directory if additional_directories: @@ -3234,7 +3316,8 @@ async def resume_session( session_id, self._client, workspace_path=None, - managed_settings_enabled=enable_managed_settings is True, + managed_settings_enabled=enable_managed_settings is True + or managed_settings is not None, ) if self._session_fs_config: if create_session_fs_handler is None: diff --git a/python/test_client.py b/python/test_client.py index f101fc396..2375bc98a 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -28,6 +28,8 @@ CloudSessionRepository, CopilotExpAssignmentResponse, ExpConfigEntry, + ManagedSettings, + ManagedSettingsPermissions, ModelBilling, ModelCapabilities, ModelInfo, @@ -651,6 +653,61 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + @pytest.mark.asyncio + async def test_create_and_resume_session_forward_managed_settings(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method in ("session.create", "session.resume"): + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + enable_managed_settings=True, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions( + disable_bypass_permissions_mode="disable", + deny=["Shell(git push)"], + ask=["Domain(publish.example)"], + allow=["Read(**)"], + ) + ), + ) + resumed_session = await client.resume_session( + session.session_id, + on_permission_request=PermissionHandler.approve_all, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions(ask=["Domain(publish.example)"]) + ), + ) + + assert session._managed_settings_enabled is True + assert resumed_session._managed_settings_enabled is True + assert captured["session.create"]["enableManagedSettings"] is True + assert captured["session.create"]["managedSettings"] == { + "permissions": { + "disableBypassPermissionsMode": "disable", + "deny": ["Shell(git push)"], + "ask": ["Domain(publish.example)"], + "allow": ["Read(**)"], + } + } + assert captured["session.resume"]["managedSettings"] == { + "permissions": {"ask": ["Domain(publish.example)"]} + } + finally: + await client.force_stop() + @pytest.mark.asyncio async def test_create_and_resume_session_default_enable_experimental_mode_by_mode(self): with TemporaryDirectory() as base_directory: @@ -691,6 +748,7 @@ async def mock_request(method, params, **kwargs): finally: await client.force_stop() + async def test_managed_settings_omitted_when_not_supplied(self): client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -698,7 +756,7 @@ async def mock_request(method, params, **kwargs): async def mock_request(method, params, **kwargs): captured[method] = params - if method in ("session.create", "session.resume"): + if method == "session.create": result = {"sessionId": params.get("sessionId") or "session-1"} callback = kwargs.get("on_response_inline") if callback is not None: @@ -707,16 +765,42 @@ async def mock_request(method, params, **kwargs): return {} client._client.request = mock_request - session = await client.create_session( + await client.create_session( on_permission_request=PermissionHandler.approve_all, ) - await client.resume_session( - session.session_id, + + assert "managedSettings" not in captured["session.create"] + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_managed_settings_preserves_empty_arrays(self): + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) + await client.start() + try: + captured = {} + + async def mock_request(method, params, **kwargs): + captured[method] = params + if method == "session.create": + result = {"sessionId": params.get("sessionId") or "session-1"} + callback = kwargs.get("on_response_inline") + if callback is not None: + callback(result) + return result + return {} + + client._client.request = mock_request + await client.create_session( on_permission_request=PermissionHandler.approve_all, + managed_settings=ManagedSettings( + permissions=ManagedSettingsPermissions(deny=[], ask=[], allow=[]) + ), ) - assert "isExperimentalMode" not in captured["session.create"] - assert "isExperimentalMode" not in captured["session.resume"] + assert captured["session.create"]["managedSettings"] == { + "permissions": {"deny": [], "ask": [], "allow": []} + } finally: await client.force_stop() diff --git a/python/test_event_forward_compatibility.py b/python/test_event_forward_compatibility.py index 1ffbd59c5..2e8015a97 100644 --- a/python/test_event_forward_compatibility.py +++ b/python/test_event_forward_compatibility.py @@ -18,10 +18,12 @@ ElicitationCompletedAction, ElicitationRequestedMode, ElicitationRequestedSchema, + ManagedSettingsResolvedSource, PermissionPromptRequestMemory, PermissionRequestMemory, PermissionRequestMemoryAction, SessionEventType, + SessionManagedSettingsResolvedData, SessionTaskCompleteData, UserMessageAgentMode, session_event_from_dict, @@ -136,6 +138,42 @@ def test_explicit_generated_symbols_remain_available(self): ) assert schema.to_dict()["type"] == "object" + def test_managed_settings_client_provenance_round_trips(self): + """Managed settings events should preserve truthful client provenance.""" + assert [source.value for source in ManagedSettingsResolvedSource] == [ + "server", + "device", + "client", + "mixed", + "none", + ] + + client = SessionManagedSettingsResolvedData( + bypass_permissions_disabled=True, + client_managed=True, + device_managed=False, + fail_closed=False, + managed_keys=["permissions"], + server_managed=False, + source=ManagedSettingsResolvedSource.CLIENT, + ) + serialized = client.to_dict() + assert serialized["source"] == "client" + assert serialized["clientManaged"] is True + assert SessionManagedSettingsResolvedData.from_dict(serialized) == client + + mixed = SessionManagedSettingsResolvedData( + bypass_permissions_disabled=True, + device_managed=True, + fail_closed=False, + managed_keys=["permissions"], + server_managed=True, + source=ManagedSettingsResolvedSource.MIXED, + ) + serialized = mixed.to_dict() + assert serialized["source"] == "mixed" + assert "clientManaged" not in serialized + def test_data_shim_preserves_raw_mapping_values(self): """Compatibility Data should keep arbitrary nested mappings as plain dicts.""" parsed = Data.from_dict( diff --git a/rust/src/session.rs b/rust/src/session.rs index d505541a5..c6c806b1c 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -66,6 +66,13 @@ pub(crate) struct SessionHandlers { pub tools: Arc>>, } +fn has_managed_settings( + enable_managed_settings: Option, + managed_settings: Option<&crate::types::ManagedSettings>, +) -> bool { + enable_managed_settings == Some(true) || managed_settings.is_some() +} + /// Shared state between a [`Session`] and its event loop, used by [`Session::send_and_wait`]. struct IdleWaiter { tx: oneshot::Sender, Error>>, @@ -899,7 +906,10 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, - managed_settings_enabled: wire.enable_managed_settings == Some(true), + managed_settings_enabled: has_managed_settings( + wire.enable_managed_settings, + wire.managed_settings.as_ref(), + ), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -1169,7 +1179,10 @@ impl Client { ); let handlers = SessionHandlers { permission: permission_handler, - managed_settings_enabled: wire.enable_managed_settings == Some(true), + managed_settings_enabled: has_managed_settings( + wire.enable_managed_settings, + wire.managed_settings.as_ref(), + ), elicitation: runtime.elicitation_handler.take(), mcp_auth: runtime.mcp_auth_handler.take(), user_input: runtime.user_input_handler.take(), @@ -2550,9 +2563,16 @@ fn inject_transform_sections_resume( mod tests { use serde_json::json; - use super::{notification_permission_payload, permission_request_data}; + use super::{has_managed_settings, notification_permission_payload, permission_request_data}; use crate::handler::PermissionResult; + #[test] + fn direct_injection_enables_managed_safeguards() { + let settings = crate::types::ManagedSettings::default(); + assert!(has_managed_settings(None, Some(&settings))); + assert!(!has_managed_settings(None, None)); + } + #[test] fn notification_payload_suppresses_no_result() { assert!(notification_permission_payload(&PermissionResult::NoResult).is_none()); diff --git a/rust/src/types.rs b/rust/src/types.rs index 37d3b248b..d3c4faa16 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1763,6 +1763,99 @@ pub struct CopilotExpAssignmentResponse { pub assignment_context: String, } +/// Controls whether bypass-permissions mode is available in a managed session. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[non_exhaustive] +pub enum DisableBypassPermissionsMode { + /// Turn off bypass-permissions mode. + Disable, +} + +/// Permission rules injected as a managed-settings layer at session bootstrap. +/// +/// All fields are optional; an omitted field imposes no constraint from this +/// layer. This layer composes restrictively with any server- or device-level +/// managed settings: [`deny`](Self::deny) and [`ask`](Self::ask) rules are +/// unioned across layers, every present [`allow`](Self::allow) list must admit a +/// tool for it to be allowed, and +/// [`disable_bypass_permissions_mode`](Self::disable_bypass_permissions_mode) is +/// honored if any layer sets it (deny-wins). +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct ManagedSettingsPermissions { + /// When set to `"disable"`, bypass-permissions mode is turned off for the + /// session regardless of other layers. Serialized as + /// `disableBypassPermissionsMode`. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_bypass_permissions_mode: Option, + /// Tool-permission patterns that are always denied. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deny: Option>, + /// Tool-permission patterns that require an explicit ask. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ask: Option>, + /// Tool-permission patterns that are allowed without prompting. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub allow: Option>, +} + +impl ManagedSettingsPermissions { + /// Sets the bypass-permissions policy for this managed layer. + pub fn with_disable_bypass_permissions_mode( + mut self, + value: DisableBypassPermissionsMode, + ) -> Self { + self.disable_bypass_permissions_mode = Some(value); + self + } + + /// Sets the rules that are always denied. + pub fn with_deny(mut self, rules: Vec) -> Self { + self.deny = Some(rules); + self + } + + /// Sets the rules that require explicit approval. + pub fn with_ask(mut self, rules: Vec) -> Self { + self.ask = Some(rules); + self + } + + /// Sets the rules that are allowed without prompting. + pub fn with_allow(mut self, rules: Vec) -> Self { + self.allow = Some(rules); + self + } +} + +/// Managed-settings layer injected at session startup. Currently carries only a +/// [`permissions`](Self::permissions) object. +/// +/// This layer is startup-only and is not persisted with the session. It must be +/// re-supplied on resume to remain in effect; omitting it on resume clears the +/// previously injected layer. It can be combined with +/// [`SessionConfig::enable_managed_settings`]. Older runtimes may ignore this +/// additive field, so hosts must not rely on injected policy until they ship a +/// compatible runtime. +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct ManagedSettings { + /// Permission rules for this managed-settings layer. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub permissions: Option, +} + +impl ManagedSettings { + /// Sets the permissions-only managed policy. + pub fn with_permissions(mut self, permissions: ManagedSettingsPermissions) -> Self { + self.permissions = Some(permissions); + self + } +} + /// Configuration for creating a new session via the `session.create` RPC. /// /// All fields are optional — the CLI applies sensible defaults. @@ -2055,6 +2148,15 @@ pub struct SessionConfig { /// (fail-closed). When `None`, behaves exactly as before. Set via /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). pub enable_managed_settings: Option, + /// Optional managed-settings layer injected at session bootstrap. Currently + /// carries a [`permissions`](ManagedSettingsPermissions) object that composes + /// restrictively with any server- or device-level managed settings. This + /// layer is startup-only and is not persisted: it must be re-supplied on + /// resume to remain in effect. Can be combined with + /// [`enable_managed_settings`](Self::enable_managed_settings). Serialized on + /// the wire as `managedSettings`. Set via + /// [`with_managed_settings`](Self::with_managed_settings). + pub managed_settings: Option, /// Custom session filesystem provider for this session. Required when /// the [`Client`](crate::Client) was started with /// [`ClientOptions::session_fs`](crate::ClientOptions::session_fs) set. @@ -2201,6 +2303,7 @@ impl std::fmt::Debug for SessionConfig { .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) .field("enable_experimental_mode", &self.enable_experimental_mode) + .field("managed_settings", &self.managed_settings) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -2312,6 +2415,7 @@ impl Default for SessionConfig { commands: None, exp_assignments: None, enable_managed_settings: None, + managed_settings: None, session_fs_provider: None, permission_handler: None, elicitation_handler: None, @@ -2477,6 +2581,7 @@ impl SessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, is_experimental_mode: self.enable_experimental_mode, + managed_settings: self.managed_settings, }; let runtime = SessionConfigRuntime { @@ -3100,6 +3205,15 @@ impl SessionConfig { self.enable_managed_settings = Some(enabled); self } + + /// Inject a managed-settings layer (currently permission rules) at session + /// bootstrap. This layer is startup-only and is not persisted, so it must be + /// re-supplied on resume to remain in effect. Can be combined with + /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). + pub fn with_managed_settings(mut self, managed_settings: ManagedSettings) -> Self { + self.managed_settings = Some(managed_settings); + self + } } /// /// See [`SessionConfig`] for the construction patterns (chained `with_*` @@ -3292,6 +3406,12 @@ pub struct ResumeSessionConfig { /// process restart. Set via /// [`with_enable_managed_settings`](Self::with_enable_managed_settings). pub enable_managed_settings: Option, + /// Optional managed-settings layer injected on resume. See + /// [`SessionConfig::managed_settings`]. This layer is not persisted, so it + /// must be re-supplied on resume to remain in effect; omitting it clears the + /// previously injected layer. Serialized on the wire as `managedSettings`. + /// Set via [`with_managed_settings`](Self::with_managed_settings). + pub managed_settings: Option, /// Custom session filesystem provider. Required on resume when the /// [`Client`](crate::Client) was started with /// [`ClientOptions::session_fs`](crate::ClientOptions::session_fs). @@ -3431,6 +3551,7 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("exp_assignments", &self.exp_assignments) .field("enable_managed_settings", &self.enable_managed_settings) .field("enable_experimental_mode", &self.enable_experimental_mode) + .field("managed_settings", &self.managed_settings) .field( "session_fs_provider", &self.session_fs_provider.as_ref().map(|_| ""), @@ -3588,6 +3709,7 @@ impl ResumeSessionConfig { exp_assignments: self.exp_assignments, enable_managed_settings: self.enable_managed_settings, is_experimental_mode: self.enable_experimental_mode, + managed_settings: self.managed_settings, suppress_resume_event: self.suppress_resume_event, continue_pending_work: self.continue_pending_work, }; @@ -3681,6 +3803,7 @@ impl ResumeSessionConfig { commands: None, exp_assignments: None, enable_managed_settings: None, + managed_settings: None, session_fs_provider: None, suppress_resume_event: None, continue_pending_work: None, @@ -4285,6 +4408,14 @@ impl ResumeSessionConfig { self.enable_managed_settings = Some(enabled); self } + + /// Inject a managed-settings layer (currently permission rules) on resume. + /// See [`SessionConfig::with_managed_settings`]. Must be re-supplied on + /// resume; omitting it clears the previously injected layer. + pub fn with_managed_settings(mut self, managed_settings: ManagedSettings) -> Self { + self.managed_settings = Some(managed_settings); + self + } } /// Controls how the system message is constructed. diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 3e19063fc..53ea1c448 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -186,6 +186,8 @@ pub(crate) struct SessionCreateWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, } /// The exact JSON shape sent on the `session.resume` JSON-RPC request. @@ -335,4 +337,6 @@ pub(crate) struct SessionResumeWire { pub enable_managed_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_experimental_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub managed_settings: Option, } diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 41cae3e95..727911081 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -17,12 +17,14 @@ use github_copilot_sdk::rpc::{ OpenCanvasInstance, }; use github_copilot_sdk::session_events::{ - McpOauthRequiredData, ReasoningSummary, SessionLimitsConfig, + ManagedSettingsResolvedSource, McpOauthRequiredData, ReasoningSummary, SessionLimitsConfig, + SessionManagedSettingsResolvedData, }; use github_copilot_sdk::types::{ CanvasProviderIdentity, CloudSessionOptions, CloudSessionRepository, CommandContext, - CommandDefinition, CommandHandler, DeliveryMode, ElicitationRequest, ElicitationResult, - ExitPlanModeData, ExtensionInfo, MessageOptions, RequestId, SessionConfig, SessionId, + CommandDefinition, CommandHandler, DeliveryMode, DisableBypassPermissionsMode, + ElicitationRequest, ElicitationResult, ExitPlanModeData, ExtensionInfo, ManagedSettings, + ManagedSettingsPermissions, MessageOptions, RequestId, SessionConfig, SessionId, SetModelOptions, Tool, ToolInvocation, ToolResult, }; use github_copilot_sdk::{Client, ContextTier, ErrorKind, ProtocolErrorKind, tool}; @@ -763,6 +765,135 @@ async fn create_session_sends_canvas_wire_fields() { timeout(TIMEOUT, create_handle).await.unwrap().unwrap(); } +#[tokio::test] +async fn create_and_resume_send_managed_settings_permissions() { + use github_copilot_sdk::types::ResumeSessionConfig; + + let (client, mut server_read, mut server_write) = make_client(); + + let managed = ManagedSettings::default().with_permissions( + ManagedSettingsPermissions::default() + .with_disable_bypass_permissions_mode(DisableBypassPermissionsMode::Disable) + .with_deny(vec!["shell(rm*)".to_string()]) + .with_ask(vec!["write".to_string()]) + .with_allow(vec![]), + ); + + let create_handle = tokio::spawn({ + let client = client.clone(); + let managed = managed.clone(); + async move { + client + .create_session( + SessionConfig::default() + .with_enable_managed_settings(true) + .with_managed_settings(managed), + ) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.create"); + assert_eq!(request["params"]["enableManagedSettings"], true); + let perms = &request["params"]["managedSettings"]["permissions"]; + assert_eq!(perms["disableBypassPermissionsMode"], "disable"); + assert_eq!(perms["deny"][0], "shell(rm*)"); + assert_eq!(perms["ask"][0], "write"); + assert_eq!(perms["allow"], serde_json::json!([])); + + let id = request["id"].as_u64().unwrap(); + let session_id = requested_session_id(&request).to_string(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "sessionId": session_id.clone() }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + timeout(TIMEOUT, create_handle).await.unwrap().unwrap(); + + let resume_handle = tokio::spawn({ + let client = client.clone(); + let session_id = session_id.clone(); + async move { + client + .resume_session( + ResumeSessionConfig::new(SessionId::from(session_id)) + .with_managed_settings(managed), + ) + .await + .unwrap() + } + }); + + let request = read_framed(&mut server_read).await; + assert_eq!(request["method"], "session.resume"); + assert_eq!( + request["params"]["managedSettings"]["permissions"]["deny"][0], + "shell(rm*)" + ); + + let id = request["id"].as_u64().unwrap(); + let response = serde_json::json!({ + "jsonrpc": "2.0", + "id": id, + "result": { "sessionId": session_id }, + }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + + let reload = read_framed(&mut server_read).await; + assert_eq!(reload["method"], "session.skills.reload"); + let id = reload["id"].as_u64().unwrap(); + let response = serde_json::json!({ "jsonrpc": "2.0", "id": id, "result": {} }); + write_framed(&mut server_write, &serde_json::to_vec(&response).unwrap()).await; + + timeout(TIMEOUT, resume_handle).await.unwrap().unwrap(); +} + +#[test] +fn managed_settings_resolved_event_preserves_client_provenance() { + let sources = [ + (ManagedSettingsResolvedSource::Server, "server"), + (ManagedSettingsResolvedSource::Device, "device"), + (ManagedSettingsResolvedSource::Client, "client"), + (ManagedSettingsResolvedSource::Mixed, "mixed"), + (ManagedSettingsResolvedSource::None, "none"), + ]; + for (source, wire_value) in sources { + assert_eq!( + serde_json::to_value(source).unwrap(), + serde_json::json!(wire_value) + ); + } + + let with_client = SessionManagedSettingsResolvedData { + bypass_permissions_disabled: true, + client_managed: Some(true), + managed_keys: vec!["permissions".to_string()], + source: ManagedSettingsResolvedSource::Client, + ..Default::default() + }; + let serialized = serde_json::to_value(&with_client).unwrap(); + assert_eq!(serialized["source"], "client"); + assert_eq!(serialized["clientManaged"], true); + + let round_tripped: SessionManagedSettingsResolvedData = + serde_json::from_value(serialized).unwrap(); + assert_eq!(round_tripped.source, ManagedSettingsResolvedSource::Client); + assert_eq!(round_tripped.client_managed, Some(true)); + + let without_client = SessionManagedSettingsResolvedData { + bypass_permissions_disabled: true, + managed_keys: vec!["permissions".to_string()], + source: ManagedSettingsResolvedSource::Mixed, + ..Default::default() + }; + let serialized = serde_json::to_value(&without_client).unwrap(); + assert_eq!(serialized["source"], "mixed"); + assert!(serialized.get("clientManaged").is_none()); +} + fn make_client_with_telemetry( callback: github_copilot_sdk::github_telemetry::GitHubTelemetryCallback, ) -> (Client, tokio::io::DuplexStream, tokio::io::DuplexStream) { From 89f126b0c53692047838d46299806787acdf74e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 7 Aug 2026 14:06:51 +0000 Subject: [PATCH 46/72] docs: update version references to 1.0.10-preview.0 --- java/README.md | 8 ++++---- java/jbang-example.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/README.md b/java/README.md index 94f2de410..8001d87d8 100644 --- a/java/README.md +++ b/java/README.md @@ -32,14 +32,14 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. com.github copilot-sdk-java - 1.0.9 + 1.0.10-preview.0 ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.9' +implementation 'com.github:copilot-sdk-java:1.0.10-preview.0' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.10-SNAPSHOT + 1.0.11-preview.0-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.10-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.11-preview.0-SNAPSHOT' ``` ## Quick Start diff --git a/java/jbang-example.java b/java/jbang-example.java index bea647ae9..39d64ad4a 100644 --- a/java/jbang-example.java +++ b/java/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.9 +//DEPS com.github:copilot-sdk-java:1.0.10-preview.0 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From db84f1063be79751f0d0188814ec9c2fc191f8fd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 7 Aug 2026 14:07:18 +0000 Subject: [PATCH 47/72] [maven-release-plugin] prepare release java/v1.0.10-preview.0 --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index ea468a75e..7113e926e 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.10-SNAPSHOT + 1.0.10-preview.0 jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.10-preview.0 From 25c0beab6095def6881bb12ddd8d36f21dcbd3d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 7 Aug 2026 14:07:22 +0000 Subject: [PATCH 48/72] [maven-release-plugin] prepare for next development iteration --- java/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 7113e926e..3a235705f 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.10-preview.0 + 1.0.11-preview.0-SNAPSHOT jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.10-preview.0 + HEAD From 243ca395b64a47d892ced939fb1884ea82a2c839 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 9 Aug 2026 08:11:55 -0400 Subject: [PATCH 49/72] Skip untyped internal C# RPC properties (#2298) Remove internal properties without a representable schema shape before C# RPC generation, while preserving typed internal properties and strict failures for public schemas. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 510b87fe-e424-4389-a811-2a4c3a2181ff --- scripts/codegen/csharp.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 2d68e68e2..4fb71cd0c 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -355,6 +355,41 @@ function failUnmappable(context: string, schema: JSONSchema7): never { ); } +function omitUntypedInternalProperties(value: unknown): void { + if (!value || typeof value !== "object") return; + if (Array.isArray(value)) { + value.forEach(omitUntypedInternalProperties); + return; + } + + const node = value as Record; + const properties = node.properties; + if (properties && typeof properties === "object" && !Array.isArray(properties)) { + for (const [name, property] of Object.entries(properties)) { + if (!property || typeof property !== "object" || Array.isArray(property)) continue; + const schema = property as JSONSchema7; + const hasType = + schema.type !== undefined || + schema.$ref !== undefined || + schema.anyOf !== undefined || + schema.oneOf !== undefined || + schema.allOf !== undefined || + schema.enum !== undefined || + schema.const !== undefined || + isOpaqueJson(schema); + if (isSchemaInternal(schema) && !hasType) { + delete (properties as Record)[name]; + } else { + omitUntypedInternalProperties(property); + } + } + } + + for (const [name, child] of Object.entries(node)) { + if (name !== "properties") omitUntypedInternalProperties(child); + } +} + function requiresArgumentNullCheck(typeName: string, isRequired: boolean): boolean { return isRequired && !typeName.endsWith("?") && !isNonNullableCSharpValueType(typeName); } @@ -2568,6 +2603,8 @@ function generateRpcCode( externalJsonSerializableRefs: Map> = new Map(), externalValueTypes: Set = new Set() ): string { + schema = cloneSchemaForCodegen(schema); + omitUntypedInternalProperties(schema); emittedRpcClassSchemas.clear(); emittedRpcEnumResultTypes.clear(); experimentalRpcTypes.clear(); From 8d0a9cc63391cb5d820bd092726c811f1225c4b9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 9 Aug 2026 10:28:18 -0400 Subject: [PATCH 50/72] Update @github/copilot to 1.0.79-9 (#2299) * Update @github/copilot to 1.0.79-9 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix factory run listing after CLI update Pass the generated RPC client's required paging request while preserving the SDK's existing listRuns API and wire behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Bound in-process .NET test cleanup Fall back to ForceStopAsync when graceful in-process fixture cleanup stalls so a hung session.destroy cannot hold the macOS runner indefinitely. Cover the concurrent graceful/forced shutdown path with a lifetime regression test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 781f5380-c114-40eb-a599-378b3f15cbea * Stabilize .NET CLI startup error test Use a deterministic failing JavaScript CLI fixture instead of relying on the bundled CLI to parse an invalid flag within the TCP startup timeout on loaded Windows runners. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 781f5380-c114-40eb-a599-378b3f15cbea --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Stephen Toub Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 781f5380-c114-40eb-a599-378b3f15cbea --- dotnet/src/Generated/Rpc.cs | 201 +++++++++--------- dotnet/src/Generated/SessionEvents.cs | 5 + dotnet/test/E2E/ClientE2ETests.cs | 10 +- dotnet/test/Harness/E2ETestContext.cs | 18 +- .../test/Unit/ClientSessionLifetimeTests.cs | 21 ++ go/rpc/zrpc.go | 87 ++++++-- go/rpc/zrpc_encoding.go | 17 ++ go/rpc/zsession_events.go | 2 + java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +++---- java/scripts/codegen/package.json | 2 +- .../generated/SubagentCompletedEvent.java | 4 +- .../generated/rpc/FactoryAgentOptions.java | 8 +- .../copilot/generated/rpc/SandboxConfig.java | 6 +- .../generated/rpc/SandboxConfigAuth.java | 29 +++ .../generated/rpc/SessionFactoryApi.java | 11 +- .../rpc/SessionFactoryListRunsParams.java | 10 +- .../rpc/SessionFactoryListRunsResult.java | 12 +- nodejs/package-lock.json | 72 +++---- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 89 ++++++-- nodejs/src/generated/session-events.ts | 4 + nodejs/src/session.ts | 2 +- python/copilot/generated/rpc.py | 152 ++++++++++--- python/copilot/generated/session_events.py | 5 + rust/src/generated/api_types.rs | 79 ++++++- rust/src/generated/rpc.rs | 14 +- rust/src/generated/session_events.rs | 3 + test/harness/package-lock.json | 72 +++---- test/harness/package.json | 2 +- 31 files changed, 712 insertions(+), 303 deletions(-) create mode 100644 java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 604ed0960..71d49d526 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -2314,11 +2314,6 @@ public sealed class SessionOpenResult [JsonPropertyName("remoteSessionId")] public string? RemoteSessionId { get; set; } - ///

In-process SessionClientApi handle for the opened session, returned to CLI callers as a transitional shortcut. Marked internal so the public SDK surface does not expose it; SDK consumers should construct per-session clients from `sessionId` instead. - [JsonInclude] - [JsonPropertyName("sessionApi")] - internal JsonElement? SessionApi { get; set; } - /// Opened session ID. Omitted when status is `not_found`. [JsonPropertyName("sessionId")] public string? SessionId { get; set; } @@ -3110,12 +3105,6 @@ public partial class RemoteControlStatusActive : RemoteControlStatus /// Whether the MC session may steer this session. [JsonPropertyName("isSteerable")] public required bool IsSteerable { get; set; } - - /// In-process prompt-manager handle (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. When the CLI migrates to a process-separated SDK, the same bidirectional prompt-routing handshake is expressed via dedicated remote-control RPCs (register/resolve) rather than a shared in-process object. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonInclude] - [JsonPropertyName("promptManager")] - internal JsonElement? PromptManager { get; set; } } /// The last setup attempt failed. The singleton is otherwise off. @@ -3266,31 +3255,18 @@ internal sealed class SessionsStopRemoteControlRequest [Experimental(Diagnostics.Experimental)] internal sealed class RegisterExtensionToolsResult { - /// In-process unsubscribe function (CLI-only optimization). Marked internal: replaced by an explicit `extensions.unregister` RPC in the SDK migration. - [JsonInclude] - [JsonPropertyName("unsubscribe")] - internal JsonElement Unsubscribe { get; set; } } /// Optional registration options. [Experimental(Diagnostics.Experimental)] public sealed class SessionsRegisterExtensionToolsOnSessionOptions { - /// In-process `() => boolean` gating callback (CLI-only optimization). Marked internal: replaced by runtime-side enable/disable RPCs in the SDK migration. - [JsonInclude] - [JsonPropertyName("enabled")] - internal JsonElement? Enabled { get; set; } } /// Params to attach an extension loader's tools to a session. [Experimental(Diagnostics.Experimental)] internal sealed class RegisterExtensionToolsParams { - /// In-process ExtensionLoader handle (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. When the CLI migrates to a process-separated SDK, extension discovery/launch moves entirely into the runtime — the CLI passes pure config (search paths, disabled ids) via SessionOptions instead. - [JsonInclude] - [JsonPropertyName("loader")] - internal JsonElement Loader { get; set; } - /// Optional registration options. [JsonPropertyName("options")] public SessionsRegisterExtensionToolsOnSessionOptions? Options { get; set; } @@ -3304,11 +3280,6 @@ internal sealed class RegisterExtensionToolsParams [Experimental(Diagnostics.Experimental)] internal sealed class ConfigureSessionExtensionsParams { - /// In-process ExtensionController delegate (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. The post-SDK extension surface exposes list/enable/disable/reload via dedicated RPCs served by the runtime. - [JsonInclude] - [JsonPropertyName("controller")] - internal JsonElement? Controller { get; set; } - /// Session to attach the extension controller delegate to. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; @@ -4309,6 +4280,7 @@ internal sealed class CanvasActionInvokeRequest [JsonDerivedType(typeof(FactoryRunFailureFactoryLimitReached), "factory_limit_reached")] [JsonDerivedType(typeof(FactoryRunFailureFactoryResumeDeclined), "factory_resume_declined")] [JsonDerivedType(typeof(FactoryRunFailureFactoryDurableFailure), "factory_durable_failure")] +[JsonDerivedType(typeof(FactoryRunFailureFactoryAccountingIncomplete), "factory_accounting_incomplete")] public partial class FactoryRunFailure { /// The type discriminator. @@ -4376,6 +4348,24 @@ public partial class FactoryRunFailureFactoryDurableFailure : FactoryRunFailure public required string RunId { get; set; } } +/// The run stopped because its usage accounting could not be completed. +/// The factory_accounting_incomplete variant of . +[Experimental(Diagnostics.Experimental)] +public partial class FactoryRunFailureFactoryAccountingIncomplete : FactoryRunFailure +{ + /// + [JsonIgnore] + public override string Type => "factory_accounting_incomplete"; + + /// Confirmed usage in nano-AIU, representing the floor of what the run spent. + [JsonPropertyName("drainedNanoAiu")] + public required long DrainedNanoAiu { get; set; } + + /// Factory run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } +} + /// Complete current or terminal factory run envelope. [Experimental(Diagnostics.Experimental)] public sealed class FactoryRunResult @@ -4660,19 +4650,47 @@ public sealed class FactoryRunSummary public long UpdatedAt { get; set; } } -/// Factory runs in durable creation order. +/// A page of factory runs in durable creation order. [Experimental(Diagnostics.Experimental)] public sealed class FactoryListRunsResult { + /// Whether terminal runs newer than this page exist. + [JsonPropertyName("hasMoreNewer")] + public bool? HasMoreNewer { get; set; } + + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } + + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } + + /// Number of terminal runs older than this page. + [JsonPropertyName("omittedOlder")] + public long? OmittedOlder { get; set; } + /// Gets or sets the runs value. [JsonPropertyName("runs")] public IList Runs { get => field ??= []; set; } } -/// Empty parameters for listing factory runs. +/// Parameters for paging factory runs. [Experimental(Diagnostics.Experimental)] internal sealed class FactoryListRunsRequest { + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } + + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } + + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; @@ -5042,6 +5060,14 @@ public sealed class FactoryAgentResult [Experimental(Diagnostics.Experimental)] public sealed class FactoryAgentOptions { + /// Optional custom agent name for the subagent. This field is accepted but not yet honored. + [JsonPropertyName("agent")] + public string? Agent { get; set; } + + /// Optional context tier for the subagent. This field is accepted but not yet honored. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } + /// Optional label distinguishing otherwise identical memoized agent calls. [JsonPropertyName("label")] public string? Label { get; set; } @@ -5050,6 +5076,10 @@ public sealed class FactoryAgentOptions [JsonPropertyName("model")] public string? Model { get; set; } + /// Optional reasoning effort for the subagent. This field is accepted but not yet honored. + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } + /// Optional JSON Schema for structured agent output. [JsonPropertyName("schema")] public JsonElement? Schema { get; set; } @@ -7162,11 +7192,6 @@ internal sealed class McpStartServersResult [Experimental(Diagnostics.Experimental)] internal sealed class McpReloadWithConfigRequest { - /// Opaque runtime MCP reload configuration. Marked internal: an in-process runtime shape (reloadMcpServers throws over the wire). - [JsonInclude] - [JsonPropertyName("config")] - internal JsonElement Config { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; @@ -7301,11 +7326,6 @@ internal sealed class McpConfigureGitHubResult [Experimental(Diagnostics.Experimental)] internal sealed class McpConfigureGitHubRequest { - /// Opaque runtime auth info for GitHub MCP configuration. Marked internal: an in-process runtime shape (configureGitHubMcp is a no-op over the wire). - [JsonInclude] - [JsonPropertyName("authInfo")] - internal JsonElement AuthInfo { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; @@ -7362,16 +7382,6 @@ internal sealed class McpStopServerRequest [Experimental(Diagnostics.Experimental)] internal sealed class McpRegisterExternalClientRequest { - /// In-process MCP Client instance. Marked internal: cannot be serialized across the JSON-RPC boundary. - [JsonInclude] - [JsonPropertyName("client")] - internal JsonElement Client { get; set; } - - /// In-process server config (MCPServerConfig) paired with the in-process client/transport. Marked internal alongside its companions. - [JsonInclude] - [JsonPropertyName("config")] - internal JsonElement Config { get; set; } - /// Logical server name for the external client. [JsonPropertyName("serverName")] public string ServerName { get; set; } = string.Empty; @@ -7379,11 +7389,6 @@ internal sealed class McpRegisterExternalClientRequest /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// In-process MCP Transport instance. Marked internal: cannot be serialized across the JSON-RPC boundary. - [JsonInclude] - [JsonPropertyName("transport")] - internal JsonElement Transport { get; set; } } /// Server name identifying the external client to remove. @@ -8649,6 +8654,19 @@ public sealed class ProviderConfig public string? WireModel { get; set; } } +/// Credential-injection capability flags applied while the sandbox is enabled. +[Experimental(Diagnostics.Experimental)] +public sealed class SandboxConfigAuth +{ + /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). + [JsonPropertyName("gh")] + public bool? Gh { get; set; } + + /// Whether to inject git credentials as an `http.<url>.extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's own helper before the sandbox is applied. Default: false (opt-in). + [JsonPropertyName("git")] + public bool? Git { get; set; } +} + /// macOS seatbelt experimental options. [Experimental(Diagnostics.Experimental)] public sealed class SandboxConfigUserPolicyExperimentalSeatbelt @@ -8764,18 +8782,14 @@ public sealed class SandboxConfig [JsonPropertyName("allowDevToolAccess")] public bool? AllowDevToolAccess { get; set; } + /// Credential-injection capability flags. + [JsonPropertyName("auth")] + public SandboxConfigAuth? Auth { get; set; } + /// Whether sandboxing is enabled for the session. [JsonPropertyName("enabled")] public bool Enabled { get; set; } - /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). - [JsonPropertyName("ghAuth")] - public bool? GhAuth { get; set; } - - /// Whether to inject the Copilot GitHub token as an `http.<host>.extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. Default: false (opt-in). - [JsonPropertyName("gitAuth")] - public bool? GitAuth { get; set; } - /// User-managed sandbox policy fragment merged into the auto-discovered base policy. [JsonPropertyName("userPolicy")] public SandboxConfigUserPolicy? UserPolicy { get; set; } @@ -10193,16 +10207,6 @@ public sealed class UIEphemeralQueryResult [Experimental(Diagnostics.Experimental)] internal sealed class UIEphemeralQueryRequest { - /// In-process `AbortSignal` forwarded to the model client to cancel an in-flight request. Marked internal: excluded from the public SDK surface. Replaced by an explicit cancellation token + cancel RPC in the SDK migration. - [JsonInclude] - [JsonPropertyName("abortSignal")] - internal JsonElement? AbortSignal { get; set; } - - /// In-process streaming callback `(text) => void` invoked with each token as the model emits it. Marked internal: excluded from the public SDK surface. In a process-separated SDK this is replaced by a streaming RPC that yields chunks and a final answer. - [JsonInclude] - [JsonPropertyName("onChunk")] - internal JsonElement? OnChunk { get; set; } - /// Question to answer from the current conversation context. [JsonPropertyName("question")] public string Question { get; set; } = string.Empty; @@ -24646,28 +24650,25 @@ public async Task GetRemoteControlStatusAsync(Cancell /// Registers extension-provided tools on the given session, gated by an optional `enabled` callback. Returns an opaque unsubscribe function the caller must invoke to deregister the tools when the extension is torn down. Marked internal because `loader`, `enabled`, and the returned `unsubscribe` are in-process handles that cannot cross the JSON-RPC boundary. Disappears once extension discovery / launch / tool registration are owned by the runtime: SDK consumers will pass pure config (search paths, disabled ids) via `SessionOptions` and the runtime will resolve, launch, register, and tear down extensions itself. /// Session to register extension tools on. - /// In-process ExtensionLoader handle (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. When the CLI migrates to a process-separated SDK, extension discovery/launch moves entirely into the runtime — the CLI passes pure config (search paths, disabled ids) via SessionOptions instead. /// Optional registration options. /// The to monitor for cancellation requests. The default is . /// Handle for releasing the extension tool registration. - internal async Task RegisterExtensionToolsOnSessionAsync(string sessionId, object loader, SessionsRegisterExtensionToolsOnSessionOptions? options = null, CancellationToken cancellationToken = default) + internal async Task RegisterExtensionToolsOnSessionAsync(string sessionId, SessionsRegisterExtensionToolsOnSessionOptions? options = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(sessionId); - ArgumentNullException.ThrowIfNull(loader); - var request = new RegisterExtensionToolsParams { SessionId = sessionId, Loader = CopilotClient.ToJsonElementForWire(loader)!.Value, Options = options }; + var request = new RegisterExtensionToolsParams { SessionId = sessionId, Options = options }; return await CopilotClient.InvokeRpcAsync(_rpc, "sessions.registerExtensionToolsOnSession", [request], cancellationToken); } /// Attaches (or detaches) an in-process ExtensionController delegate for the given session, used by shared-API surfaces that need to query or modify the session's extension state. Pass `controller: undefined` to detach. Marked internal because the controller is an in-process object that cannot cross the JSON-RPC boundary. Disappears alongside `registerExtensionToolsOnSession`: once the runtime owns extension management, the public surface exposes list/enable/disable/reload as dedicated RPCs served by the runtime. /// Session to attach the extension controller delegate to. - /// In-process ExtensionController delegate (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. The post-SDK extension surface exposes list/enable/disable/reload via dedicated RPCs served by the runtime. /// The to monitor for cancellation requests. The default is . - internal async Task ConfigureSessionExtensionsAsync(string sessionId, object? controller = null, CancellationToken cancellationToken = default) + internal async Task ConfigureSessionExtensionsAsync(string sessionId, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(sessionId); - var request = new ConfigureSessionExtensionsParams { SessionId = sessionId, Controller = CopilotClient.ToJsonElementForWire(controller) }; + var request = new ConfigureSessionExtensionsParams { SessionId = sessionId }; await CopilotClient.InvokeRpcAsync(_rpc, "sessions.configureSessionExtensions", [request], cancellationToken); } } @@ -25299,13 +25300,16 @@ public async Task GetRunAsync(string runId, CancellationToken } /// Lists durable factory runs for this session in creation order. + /// Exclusive forward cursor. + /// Exclusive backward cursor. + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. /// The to monitor for cancellation requests. The default is . - /// Factory runs in durable creation order. - public async Task ListRunsAsync(CancellationToken cancellationToken = default) + /// A page of factory runs in durable creation order. + public async Task ListRunsAsync(long? afterSeq = null, long? beforeSeq = null, int? limit = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new FactoryListRunsRequest { SessionId = _session.SessionId }; + var request = new FactoryListRunsRequest { SessionId = _session.SessionId, AfterSeq = afterSeq, BeforeSeq = beforeSeq, Limit = limit }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.factory.listRuns", [request], cancellationToken); } @@ -26337,15 +26341,13 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) } /// Reloads MCP server connections for the session with an explicit host-provided configuration. - /// Opaque runtime MCP reload configuration. Marked internal: an in-process runtime shape (reloadMcpServers throws over the wire). /// The to monitor for cancellation requests. The default is . /// MCP server startup filtering result. - internal async Task ReloadWithConfigAsync(object config, CancellationToken cancellationToken = default) + internal async Task ReloadWithConfigAsync(CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(config); _session.ThrowIfDisposed(); - var request = new McpReloadWithConfigRequest { SessionId = _session.SessionId, Config = CopilotClient.ToJsonElementForWire(config)!.Value }; + var request = new McpReloadWithConfigRequest { SessionId = _session.SessionId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.reloadWithConfig", [request], cancellationToken); } @@ -26405,15 +26407,13 @@ public async Task RemoveGitHubAsync(CancellationToken can } /// Configures the built-in GitHub MCP server for the session's current auth context. - /// Opaque runtime auth info for GitHub MCP configuration. Marked internal: an in-process runtime shape (configureGitHubMcp is a no-op over the wire). /// The to monitor for cancellation requests. The default is . /// Result of configuring GitHub MCP. - internal async Task ConfigureGitHubAsync(object authInfo, CancellationToken cancellationToken = default) + internal async Task ConfigureGitHubAsync(CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(authInfo); _session.ThrowIfDisposed(); - var request = new McpConfigureGitHubRequest { SessionId = _session.SessionId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo)!.Value }; + var request = new McpConfigureGitHubRequest { SessionId = _session.SessionId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.configureGitHub", [request], cancellationToken); } @@ -26457,19 +26457,13 @@ public async Task StopServerAsync(string serverName, CancellationToken cancellat /// Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself. /// Logical server name for the external client. - /// In-process MCP Client instance. Marked internal: cannot be serialized across the JSON-RPC boundary. - /// In-process MCP Transport instance. Marked internal: cannot be serialized across the JSON-RPC boundary. - /// In-process server config (MCPServerConfig) paired with the in-process client/transport. Marked internal alongside its companions. /// The to monitor for cancellation requests. The default is . - internal async Task RegisterExternalClientAsync(string serverName, object client, object transport, object config, CancellationToken cancellationToken = default) + internal async Task RegisterExternalClientAsync(string serverName, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(serverName); - ArgumentNullException.ThrowIfNull(client); - ArgumentNullException.ThrowIfNull(transport); - ArgumentNullException.ThrowIfNull(config); _session.ThrowIfDisposed(); - var request = new McpRegisterExternalClientRequest { SessionId = _session.SessionId, ServerName = serverName, Client = CopilotClient.ToJsonElementForWire(client)!.Value, Transport = CopilotClient.ToJsonElementForWire(transport)!.Value, Config = CopilotClient.ToJsonElementForWire(config)!.Value }; + var request = new McpRegisterExternalClientRequest { SessionId = _session.SessionId, ServerName = serverName }; await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.registerExternalClient", [request], cancellationToken); } @@ -27223,16 +27217,14 @@ internal UiApi(CopilotSession session) /// Runs a transient no-tools model query against the current conversation context. /// Question to answer from the current conversation context. - /// In-process streaming callback `(text) => void` invoked with each token as the model emits it. Marked internal: excluded from the public SDK surface. In a process-separated SDK this is replaced by a streaming RPC that yields chunks and a final answer. - /// In-process `AbortSignal` forwarded to the model client to cancel an in-flight request. Marked internal: excluded from the public SDK surface. Replaced by an explicit cancellation token + cancel RPC in the SDK migration. /// The to monitor for cancellation requests. The default is . /// Transient answer generated from current conversation context. - public async Task EphemeralQueryAsync(string question, object? onChunk = null, object? abortSignal = null, CancellationToken cancellationToken = default) + public async Task EphemeralQueryAsync(string question, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(question); _session.ThrowIfDisposed(); - var request = new UIEphemeralQueryRequest { SessionId = _session.SessionId, Question = question, OnChunk = CopilotClient.ToJsonElementForWire(onChunk), AbortSignal = CopilotClient.ToJsonElementForWire(abortSignal) }; + var request = new UIEphemeralQueryRequest { SessionId = _session.SessionId, Question = question }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.ephemeralQuery", [request], cancellationToken); } @@ -29818,6 +29810,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(RemoteSessionMetadataValue))] [JsonSerializable(typeof(RunOptions))] [JsonSerializable(typeof(SandboxConfig))] +[JsonSerializable(typeof(SandboxConfigAuth))] [JsonSerializable(typeof(SandboxConfigUserPolicy))] [JsonSerializable(typeof(SandboxConfigUserPolicyExperimental))] [JsonSerializable(typeof(SandboxConfigUserPolicyExperimentalSeatbelt))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 47c13846f..90eb08263 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -3553,6 +3553,11 @@ public sealed partial class SubagentCompletedData [JsonPropertyName("agentName")] public required string AgentName { get; set; } + /// Whether the sub-agent was torn down by cancellation - its own abort, or an ancestor being killed - instead of finishing its work. Cancellation is not a failure, so the run still reports completion; this distinguishes a torn-down sub-agent from one that ran to the end. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cancelled")] + public bool? Cancelled { get; set; } + /// Wall-clock duration of the sub-agent execution in milliseconds. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] diff --git a/dotnet/test/E2E/ClientE2ETests.cs b/dotnet/test/E2E/ClientE2ETests.cs index d166223d6..b6bdfd90f 100644 --- a/dotnet/test/E2E/ClientE2ETests.cs +++ b/dotnet/test/E2E/ClientE2ETests.cs @@ -11,6 +11,9 @@ namespace GitHub.Copilot.Test.E2E; // Other test classes should instead inherit from E2ETestBase public class ClientE2ETests(E2ETestFixture fixture) : IClassFixture { + private const string FailingCliScript = + "process.stderr.write('nonexistent test flag on stderr\\n'); process.exit(1);"; + private E2ETestContext Ctx => fixture.Ctx; [Theory] @@ -177,11 +180,14 @@ public async Task Should_Not_Throw_When_Disposing_Session_After_Stopping_Client( [InlineData(false)] // TCP transport public async Task Should_Report_Error_With_Stderr_When_CLI_Fails_To_Start(bool useStdio) { + var cliPath = Path.Join(Ctx.WorkDir, $"failing-cli-{Guid.NewGuid():N}.js"); + await File.WriteAllTextAsync(cliPath, FailingCliScript); + var client = new CopilotClient(new CopilotClientOptions { Connection = useStdio - ? RuntimeConnection.ForStdio(args: ["--nonexistent-flag-for-testing"]) - : RuntimeConnection.ForTcp(args: ["--nonexistent-flag-for-testing"]) + ? RuntimeConnection.ForStdio(path: cliPath) + : RuntimeConnection.ForTcp(path: cliPath) }); var ex = await Assert.ThrowsAsync(() => client.StartAsync()); diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs index af34de6ec..1c88d809b 100644 --- a/dotnet/test/Harness/E2ETestContext.cs +++ b/dotnet/test/Harness/E2ETestContext.cs @@ -13,6 +13,7 @@ namespace GitHub.Copilot.Test.Harness; public sealed class E2ETestContext : IAsyncDisposable { private const string DefaultGitHubToken = "fake-token-for-e2e-tests"; + private static readonly TimeSpan s_gracefulClientStopTimeout = TimeSpan.FromSeconds(30); public string HomeDir { get; } public string WorkDir { get; } @@ -549,7 +550,6 @@ private static bool IsInProcess(RuntimeConnection? connection) return false; } - // Inproc holds the session-store SQLite handle in-process; graceful StopAsync releases it so the temp-dir delete succeeds on Windows. private static async Task StopClientForCleanupAsync(CopilotClient client) { var isInProcess = string.Equals( @@ -558,7 +558,21 @@ private static async Task StopClientForCleanupAsync(CopilotClient client) StringComparison.OrdinalIgnoreCase); if (isInProcess) { - await client.StopAsync(); + var gracefulStop = client.StopAsync(); + try + { + await gracefulStop.WaitAsync(s_gracefulClientStopTimeout); + } + catch (TimeoutException) + { + Console.Error.WriteLine( + $"Graceful in-process client cleanup exceeded {s_gracefulClientStopTimeout}; forcing shutdown."); + await client.ForceStopAsync(); + + // Disposing the connection completes any session.destroy RPC that + // blocked graceful cleanup. Observe that task before continuing. + await gracefulStop.WaitAsync(s_gracefulClientStopTimeout); + } } else { diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index d4b4100b4..da04ff38b 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -183,6 +183,27 @@ public async Task StopAsync_Keeps_Session_Rooted_Until_Destroy_Completes() AssertSessionCount(client, sessions: 0); } + [Fact] + public async Task ForceStopAsync_Unblocks_StopAsync_When_Session_Destroy_Hangs() + { + await using var server = await FakeCopilotServer.StartAsync(); + server.DelayDestroy(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + + _ = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll + }); + + var stopTask = client.StopAsync(); + await server.DestroyStarted; + + await client.ForceStopAsync(); + await stopTask.WaitAsync(TimeSpan.FromSeconds(5)); + + AssertSessionCount(client, sessions: 0); + } + [Fact] public async Task ResumeSessionAsync_Throws_When_Same_Client_Already_Tracks_Session() { diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 6de48d06c..622c7a0cb 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -2452,10 +2452,16 @@ type FactoryAckResult struct { // Experimental: FactoryAgentOptions is part of an experimental API and may change or be // removed. type FactoryAgentOptions struct { + // Optional custom agent name for the subagent. This field is accepted but not yet honored. + Agent *string `json:"agent,omitempty"` + // Optional context tier for the subagent. This field is accepted but not yet honored. + ContextTier *ContextTier `json:"contextTier,omitempty"` // Optional label distinguishing otherwise identical memoized agent calls. Label *string `json:"label,omitempty"` // Optional model identifier for the subagent. Model *string `json:"model,omitempty"` + // Optional reasoning effort for the subagent. This field is accepted but not yet honored. + ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Optional JSON Schema for structured agent output. Schema any `json:"schema,omitempty"` } @@ -2611,17 +2617,31 @@ type FactoryJournalPutRequest struct { RunID string `json:"runId"` } -// Empty parameters for listing factory runs. +// Parameters for paging factory runs. // Experimental: FactoryListRunsRequest is part of an experimental API and may change or be // removed. type FactoryListRunsRequest struct { + // Exclusive forward cursor. + AfterSeq *int64 `json:"afterSeq,omitempty"` + // Exclusive backward cursor. + BeforeSeq *int64 `json:"beforeSeq,omitempty"` + // Maximum terminal runs to return. Defaults to 200 and is capped at 500. + Limit *int32 `json:"limit,omitempty"` } -// Factory runs in durable creation order. +// A page of factory runs in durable creation order. // Experimental: FactoryListRunsResult is part of an experimental API and may change or be // removed. type FactoryListRunsResult struct { - Runs []FactoryRunSummary `json:"runs"` + // Whether terminal runs newer than this page exist. + HasMoreNewer *bool `json:"hasMoreNewer,omitempty"` + // Newest terminal-run cursor in this page, or null when the terminal window is empty. + NewestSeq *int64 `json:"newestSeq,omitempty"` + // Oldest terminal-run cursor in this page, or null when the terminal window is empty. + OldestSeq *int64 `json:"oldestSeq,omitempty"` + // Number of terminal runs older than this page. + OmittedOlder *int64 `json:"omittedOlder,omitempty"` + Runs []FactoryRunSummary `json:"runs"` } // One ordered factory progress line. @@ -2772,6 +2792,19 @@ func (r RawFactoryRunFailureData) Type() FactoryRunFailureType { return r.Discriminator } +// The run stopped because its usage accounting could not be completed. +type FactoryRunFailureFactoryAccountingIncomplete struct { + // Confirmed usage in nano-AIU, representing the floor of what the run spent. + DrainedNanoAiu int64 `json:"drainedNanoAiu"` + // Factory run identifier. + RunID string `json:"runId"` +} + +func (FactoryRunFailureFactoryAccountingIncomplete) factoryRunFailure() {} +func (FactoryRunFailureFactoryAccountingIncomplete) Type() FactoryRunFailureType { + return FactoryRunFailureTypeFactoryAccountingIncomplete +} + type FactoryRunFailureFactoryDurableFailure struct { // Stable failure code. Code string `json:"code"` @@ -8208,19 +8241,29 @@ type SandboxConfig struct { // Cargo lock files granted read-write. Default: true (enabled by default; set to false to // opt out). AllowDevToolAccess *bool `json:"allowDevToolAccess,omitempty"` + // Credential-injection capability flags. + Auth *SandboxConfigAuth `json:"auth,omitempty"` // Whether sandboxing is enabled for the session. Enabled bool `json:"enabled"` - // Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the - // OS keyring the sandbox blocks. Default: false (opt-in). - GhAuth *bool `json:"ghAuth,omitempty"` - // Whether to inject the Copilot GitHub token as an `http..extraheader` so - // authenticated HTTPS git works inside the sandbox without the shell-based credential - // helper the sandbox blocks. Default: false (opt-in). - GitAuth *bool `json:"gitAuth,omitempty"` // User-managed sandbox policy fragment merged into the auto-discovered base policy. UserPolicy *SandboxConfigUserPolicy `json:"userPolicy,omitempty"` } +// Credential-injection capability flags applied while the sandbox is enabled. +// Experimental: SandboxConfigAuth is part of an experimental API and may change or be +// removed. +type SandboxConfigAuth struct { + // Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the + // OS keyring the sandbox blocks. Default: false (opt-in). + Gh *bool `json:"gh,omitempty"` + // Whether to inject git credentials as an `http..extraheader` so authenticated HTTPS + // git works inside the sandbox without the shell-based credential helper the sandbox + // blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, + // GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's + // own helper before the sandbox is applied. Default: false (opt-in). + Git *bool `json:"git,omitempty"` +} + // User-managed sandbox policy fragment merged into the auto-discovered base policy. // Experimental: SandboxConfigUserPolicy is part of an experimental API and may change or be // removed. @@ -13525,9 +13568,10 @@ const ( type FactoryRunFailureType string const ( - FactoryRunFailureTypeFactoryDurableFailure FactoryRunFailureType = "factory_durable_failure" - FactoryRunFailureTypeFactoryLimitReached FactoryRunFailureType = "factory_limit_reached" - FactoryRunFailureTypeFactoryResumeDeclined FactoryRunFailureType = "factory_resume_declined" + FactoryRunFailureTypeFactoryAccountingIncomplete FactoryRunFailureType = "factory_accounting_incomplete" + FactoryRunFailureTypeFactoryDurableFailure FactoryRunFailureType = "factory_durable_failure" + FactoryRunFailureTypeFactoryLimitReached FactoryRunFailureType = "factory_limit_reached" + FactoryRunFailureTypeFactoryResumeDeclined FactoryRunFailureType = "factory_resume_declined" ) // Current or terminal state of a factory run. @@ -18218,9 +18262,22 @@ func (a *FactoryAPI) GetRunProgress(ctx context.Context, params *FactoryGetRunPr // // RPC method: session.factory.listRuns. // -// Returns: Factory runs in durable creation order. -func (a *FactoryAPI) ListRuns(ctx context.Context) (*FactoryListRunsResult, error) { +// Parameters: Parameters for paging factory runs. +// +// Returns: A page of factory runs in durable creation order. +func (a *FactoryAPI) ListRuns(ctx context.Context, params *FactoryListRunsRequest) (*FactoryListRunsResult, error) { req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.AfterSeq != nil { + req["afterSeq"] = *params.AfterSeq + } + if params.BeforeSeq != nil { + req["beforeSeq"] = *params.BeforeSeq + } + if params.Limit != nil { + req["limit"] = *params.Limit + } + } raw, err := a.client.Request(ctx, "session.factory.listRuns", req) if err != nil { return nil, err diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 8b69c28c2..29c253e1c 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -1075,6 +1075,12 @@ func unmarshalFactoryRunFailure(data []byte) (FactoryRunFailure, error) { } switch raw.Type { + case FactoryRunFailureTypeFactoryAccountingIncomplete: + var d FactoryRunFailureFactoryAccountingIncomplete + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case FactoryRunFailureTypeFactoryDurableFailure: var d FactoryRunFailureFactoryDurableFailure if err := json.Unmarshal(data, &d); err != nil { @@ -1109,6 +1115,17 @@ func (r RawFactoryRunFailureData) MarshalJSON() ([]byte, error) { }) } +func (r FactoryRunFailureFactoryAccountingIncomplete) MarshalJSON() ([]byte, error) { + type alias FactoryRunFailureFactoryAccountingIncomplete + return json.Marshal(struct { + Type FactoryRunFailureType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r FactoryRunFailureFactoryDurableFailure) MarshalJSON() ([]byte, error) { type alias FactoryRunFailureFactoryDurableFailure return json.Marshal(struct { diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 0034af6a7..05c8fd548 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -1882,6 +1882,8 @@ type SubagentCompletedData struct { AgentDisplayName string `json:"agentDisplayName"` // Internal name of the sub-agent AgentName string `json:"agentName"` + // Whether the sub-agent was torn down by cancellation - its own abort, or an ancestor being killed - instead of finishing its work. Cancellation is not a failure, so the run still reports completion; this distinguishes a torn-down sub-agent from one that ran to the end. + Cancelled *bool `json:"cancelled,omitempty"` // Wall-clock duration of the sub-agent execution in milliseconds DurationMs *int64 `json:"durationMs,omitempty"` // Model used by the sub-agent diff --git a/java/pom.xml b/java/pom.xml index 3a235705f..c5e9f62ad 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.79-6 + ^1.0.79-9 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index 5b3197f6f..d4824feba 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", - "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", + "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-6", - "@github/copilot-darwin-x64": "1.0.79-6", - "@github/copilot-linux-arm64": "1.0.79-6", - "@github/copilot-linux-x64": "1.0.79-6", - "@github/copilot-linuxmusl-arm64": "1.0.79-6", - "@github/copilot-linuxmusl-x64": "1.0.79-6", - "@github/copilot-win32-arm64": "1.0.79-6", - "@github/copilot-win32-x64": "1.0.79-6" + "@github/copilot-darwin-arm64": "1.0.79-9", + "@github/copilot-darwin-x64": "1.0.79-9", + "@github/copilot-linux-arm64": "1.0.79-9", + "@github/copilot-linux-x64": "1.0.79-9", + "@github/copilot-linuxmusl-arm64": "1.0.79-9", + "@github/copilot-linuxmusl-x64": "1.0.79-9", + "@github/copilot-win32-arm64": "1.0.79-9", + "@github/copilot-win32-x64": "1.0.79-9" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", - "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", + "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", - "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", + "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", - "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", + "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", - "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", + "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", - "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", + "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", - "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", + "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", - "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", + "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", - "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", + "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index bb8936e75..5e3630ec0 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java b/java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java index f32613579..f7300ddbf 100644 --- a/java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java +++ b/java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java @@ -47,7 +47,9 @@ public record SubagentCompletedEventData( /** Total tokens (input + output) consumed by the sub-agent */ @JsonProperty("totalTokens") Long totalTokens, /** Wall-clock duration of the sub-agent execution in milliseconds */ - @JsonProperty("durationMs") Long durationMs + @JsonProperty("durationMs") Long durationMs, + /** Whether the sub-agent was torn down by cancellation - its own abort, or an ancestor being killed - instead of finishing its work. Cancellation is not a failure, so the run still reports completion; this distinguishes a torn-down sub-agent from one that ran to the end. */ + @JsonProperty("cancelled") Boolean cancelled ) { } } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java b/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java index 675e715e8..9910d4f7f 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java @@ -26,6 +26,12 @@ public record FactoryAgentOptions( /** Optional JSON Schema for structured agent output. */ @JsonProperty("schema") Object schema, /** Optional model identifier for the subagent. */ - @JsonProperty("model") String model + @JsonProperty("model") String model, + /** Optional reasoning effort for the subagent. This field is accepted but not yet honored. */ + @JsonProperty("reasoningEffort") String reasoningEffort, + /** Optional context tier for the subagent. This field is accepted but not yet honored. */ + @JsonProperty("contextTier") ContextTier contextTier, + /** Optional custom agent name for the subagent. This field is accepted but not yet honored. */ + @JsonProperty("agent") String agent ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java index cfb2c23ff..d9130eb81 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java @@ -27,10 +27,8 @@ public record SandboxConfig( @JsonProperty("userPolicy") SandboxConfigUserPolicy userPolicy, /** Whether to auto-add the current working directory to readwritePaths. Default: true. */ @JsonProperty("addCurrentWorkingDirectory") Boolean addCurrentWorkingDirectory, - /** Whether to inject the Copilot GitHub token as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. Default: false (opt-in). */ - @JsonProperty("gitAuth") Boolean gitAuth, - /** Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ - @JsonProperty("ghAuth") Boolean ghAuth, + /** Credential-injection capability flags. */ + @JsonProperty("auth") SandboxConfigAuth auth, /** Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ @JsonProperty("allowDevToolAccess") Boolean allowDevToolAccess ) { diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java new file mode 100644 index 000000000..4a3612e0b --- /dev/null +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Credential-injection capability flags applied while the sandbox is enabled. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SandboxConfigAuth( + /** Whether to inject git credentials as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's own helper before the sandbox is applied. Default: false (opt-in). */ + @JsonProperty("git") Boolean git, + /** Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). */ + @JsonProperty("gh") Boolean gh +) { +} diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java index 6d15ac17c..e0628ea3d 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java @@ -83,14 +83,19 @@ public CompletableFuture getRun(SessionFactoryGetRun } /** - * Empty parameters for listing factory runs. + * Parameters for paging factory runs. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture listRuns() { - return caller.invoke("session.factory.listRuns", java.util.Map.of("sessionId", this.sessionId), SessionFactoryListRunsResult.class); + public CompletableFuture listRuns(SessionFactoryListRunsParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.factory.listRuns", _p, SessionFactoryListRunsResult.class); } /** diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java index e3b90f07f..41de4ae4a 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Empty parameters for listing factory runs. + * Parameters for paging factory runs. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -25,6 +25,12 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionFactoryListRunsParams( /** Target session identifier */ - @JsonProperty("sessionId") String sessionId + @JsonProperty("sessionId") String sessionId, + /** Exclusive forward cursor. */ + @JsonProperty("afterSeq") Long afterSeq, + /** Exclusive backward cursor. */ + @JsonProperty("beforeSeq") Long beforeSeq, + /** Maximum terminal runs to return. Defaults to 200 and is capped at 500. */ + @JsonProperty("limit") Long limit ) { } diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java index 7d1ac45a9..3a23bc369 100644 --- a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java +++ b/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java @@ -15,7 +15,7 @@ import javax.annotation.processing.Generated; /** - * Factory runs in durable creation order. + * A page of factory runs in durable creation order. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -25,6 +25,14 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SessionFactoryListRunsResult( - @JsonProperty("runs") List runs + @JsonProperty("runs") List runs, + /** Oldest terminal-run cursor in this page, or null when the terminal window is empty. */ + @JsonProperty("oldestSeq") Long oldestSeq, + /** Newest terminal-run cursor in this page, or null when the terminal window is empty. */ + @JsonProperty("newestSeq") Long newestSeq, + /** Whether terminal runs newer than this page exist. */ + @JsonProperty("hasMoreNewer") Boolean hasMoreNewer, + /** Number of terminal runs older than this page. */ + @JsonProperty("omittedOlder") Long omittedOlder ) { } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index cd571ced1..39fa8b6fa 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", - "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", + "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-6", - "@github/copilot-darwin-x64": "1.0.79-6", - "@github/copilot-linux-arm64": "1.0.79-6", - "@github/copilot-linux-x64": "1.0.79-6", - "@github/copilot-linuxmusl-arm64": "1.0.79-6", - "@github/copilot-linuxmusl-x64": "1.0.79-6", - "@github/copilot-win32-arm64": "1.0.79-6", - "@github/copilot-win32-x64": "1.0.79-6" + "@github/copilot-darwin-arm64": "1.0.79-9", + "@github/copilot-darwin-x64": "1.0.79-9", + "@github/copilot-linux-arm64": "1.0.79-9", + "@github/copilot-linux-x64": "1.0.79-9", + "@github/copilot-linuxmusl-arm64": "1.0.79-9", + "@github/copilot-linuxmusl-x64": "1.0.79-9", + "@github/copilot-win32-arm64": "1.0.79-9", + "@github/copilot-win32-x64": "1.0.79-9" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", - "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", + "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", - "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", + "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", - "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", + "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", - "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", + "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", - "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", + "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", - "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", + "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", - "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", + "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", - "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", + "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 5becc3d3c..a4160cd2c 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 5794a0032..ad8403002 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 7a8c66909..042a7d0bb 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -686,6 +686,17 @@ export type FactoryRunFailure = */ runId: string; type: "factory_durable_failure"; + } + | { + /** + * Factory run identifier. + */ + runId: string; + /** + * Confirmed usage in nano-AIU, representing the floor of what the run spent. + */ + drainedNanoAiu: number; + type: "factory_accounting_incomplete"; }; /** * Cumulative resource ceiling that stopped a factory run. @@ -5733,6 +5744,15 @@ export interface FactoryAgentOptions { * Optional model identifier for the subagent. */ model?: string; + /** + * Optional reasoning effort for the subagent. This field is accepted but not yet honored. + */ + reasoningEffort?: string; + contextTier?: ContextTier; + /** + * Optional custom agent name for the subagent. This field is accepted but not yet honored. + */ + agent?: string; } /** * Parameters for one factory-scoped subagent call. @@ -5986,15 +6006,28 @@ export interface FactoryJournalPutRequest { }; } /** - * Empty parameters for listing factory runs. + * Parameters for paging factory runs. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "FactoryListRunsRequest". */ /** @experimental */ -export interface FactoryListRunsRequest {} +export interface FactoryListRunsRequest { + /** + * Exclusive forward cursor. + */ + afterSeq?: number; + /** + * Exclusive backward cursor. + */ + beforeSeq?: number; + /** + * Maximum terminal runs to return. Defaults to 200 and is capped at 500. + */ + limit?: number; +} /** - * Factory runs in durable creation order. + * A page of factory runs in durable creation order. * * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema * via the `definition` "FactoryListRunsResult". @@ -6002,6 +6035,22 @@ export interface FactoryListRunsRequest {} /** @experimental */ export interface FactoryListRunsResult { runs: FactoryRunSummary[]; + /** + * Oldest terminal-run cursor in this page, or null when the terminal window is empty. + */ + oldestSeq?: number | null; + /** + * Newest terminal-run cursor in this page, or null when the terminal window is empty. + */ + newestSeq?: number | null; + /** + * Whether terminal runs newer than this page exist. + */ + hasMoreNewer?: boolean; + /** + * Number of terminal runs older than this page. + */ + omittedOlder?: number; } /** * Durable factory run summary with read-time live overlays. @@ -13240,14 +13289,7 @@ export interface SandboxConfig { * Whether to auto-add the current working directory to readwritePaths. Default: true. */ addCurrentWorkingDirectory?: boolean; - /** - * Whether to inject the Copilot GitHub token as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. Default: false (opt-in). - */ - gitAuth?: boolean; - /** - * Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). - */ - ghAuth?: boolean; + auth?: SandboxConfigAuth; /** * Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). */ @@ -13366,6 +13408,23 @@ export interface SandboxConfigUserPolicyExperimentalSeatbelt { */ keychainAccess?: boolean; } +/** + * Credential-injection capability flags applied while the sandbox is enabled. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "SandboxConfigAuth". + */ +/** @experimental */ +export interface SandboxConfigAuth { + /** + * Whether to inject git credentials as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's own helper before the sandbox is applied. Default: false (opt-in). + */ + git?: boolean; + /** + * Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). + */ + gh?: boolean; +} /** * Register an absolute-time scheduled prompt. * @@ -19726,10 +19785,12 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin /** * Lists durable factory runs for this session in creation order. * - * @returns Factory runs in durable creation order. + * @param params Parameters for paging factory runs. + * + * @returns A page of factory runs in durable creation order. */ - listRuns: async (): Promise => - connection.sendRequest("session.factory.listRuns", { sessionId }), + listRuns: async (params: FactoryListRunsRequest): Promise => + connection.sendRequest("session.factory.listRuns", { sessionId, ...params }), /** * Gets durable and live observability detail for one factory run. * diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 19d044d6d..db24fc23e 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -5561,6 +5561,10 @@ export interface SubagentCompletedData { * Internal name of the sub-agent */ agentName: string; + /** + * Whether the sub-agent was torn down by cancellation - its own abort, or an ancestor being killed - instead of finishing its work. Cancellation is not a failure, so the run still reports completion; this distinguishes a torn-down sub-agent from one that ran to the end. + */ + cancelled?: boolean; /** * Wall-clock duration of the sub-agent execution in milliseconds */ diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index ed575a515..5cc49fb75 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -487,7 +487,7 @@ export class CopilotSession { }) as SessionFactoryApi["resume"], getRun: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.getRun({ runId })), waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal), - listRuns: async () => (await this.rpc.factory.listRuns()).runs, + listRuns: async () => (await this.rpc.factory.listRuns({})).runs, getRunDetail: (runId) => this.rpc.factory.getRunDetail({ runId }), getRunProgress: (runId, options = {}) => this.rpc.factory.getRunProgress({ runId, ...options }), diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index a9511540c..103149088 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -2284,29 +2284,47 @@ class FactoryAgentOptions: Subagent execution options. """ + agent: str | None = None + """Optional custom agent name for the subagent. This field is accepted but not yet honored.""" + + context_tier: ContextTier | None = None + """Optional context tier for the subagent. This field is accepted but not yet honored.""" + label: str | None = None """Optional label distinguishing otherwise identical memoized agent calls.""" model: str | None = None """Optional model identifier for the subagent.""" + reasoning_effort: str | None = None + """Optional reasoning effort for the subagent. This field is accepted but not yet honored.""" + schema: Any = None """Optional JSON Schema for structured agent output.""" @staticmethod def from_dict(obj: Any) -> 'FactoryAgentOptions': assert isinstance(obj, dict) + agent = from_union([from_str, from_none], obj.get("agent")) + context_tier = from_union([ContextTier, from_none], obj.get("contextTier")) label = from_union([from_str, from_none], obj.get("label")) model = from_union([from_str, from_none], obj.get("model")) + reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) schema = obj.get("schema") - return FactoryAgentOptions(label, model, schema) + return FactoryAgentOptions(agent, context_tier, label, model, reasoning_effort, schema) def to_dict(self) -> dict: result: dict = {} + if self.agent is not None: + result["agent"] = from_union([from_str, from_none], self.agent) + if self.context_tier is not None: + result["contextTier"] = from_union([lambda x: to_enum(ContextTier, x), from_none], self.context_tier) if self.label is not None: result["label"] = from_union([from_str, from_none], self.label) if self.model is not None: result["model"] = from_union([from_str, from_none], self.model) + if self.reasoning_effort is not None: + result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) if self.schema is not None: result["schema"] = self.schema return result @@ -2690,14 +2708,33 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class FactoryListRunsRequest: - """Empty parameters for listing factory runs.""" + """Parameters for paging factory runs.""" + + after_seq: int | None = None + """Exclusive forward cursor.""" + + before_seq: int | None = None + """Exclusive backward cursor.""" + + limit: int | None = None + """Maximum terminal runs to return. Defaults to 200 and is capped at 500.""" + @staticmethod def from_dict(obj: Any) -> 'FactoryListRunsRequest': assert isinstance(obj, dict) - return FactoryListRunsRequest() + after_seq = from_union([from_int, from_none], obj.get("afterSeq")) + before_seq = from_union([from_int, from_none], obj.get("beforeSeq")) + limit = from_union([from_int, from_none], obj.get("limit")) + return FactoryListRunsRequest(after_seq, before_seq, limit) def to_dict(self) -> dict: result: dict = {} + if self.after_seq is not None: + result["afterSeq"] = from_union([from_int, from_none], self.after_seq) + if self.before_seq is not None: + result["beforeSeq"] = from_union([from_int, from_none], self.before_seq) + if self.limit is not None: + result["limit"] = from_union([from_int, from_none], self.limit) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -2748,6 +2785,7 @@ class FactoryRunFailureKind(Enum): TIMEOUT_SECONDS = "timeoutSeconds" class FactoryRunFailureType(Enum): + FACTORY_ACCOUNTING_INCOMPLETE = "factory_accounting_incomplete" FACTORY_DURABLE_FAILURE = "factory_durable_failure" FACTORY_LIMIT_REACHED = "factory_limit_reached" FACTORY_RESUME_DECLINED = "factory_resume_declined" @@ -8102,6 +8140,40 @@ def to_dict(self) -> dict: result["branch"] = from_union([from_str, from_none], self.branch) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SandboxConfigAuth: + """Credential-injection capability flags. + + Credential-injection capability flags applied while the sandbox is enabled. + """ + gh: bool | None = None + """Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the + OS keyring the sandbox blocks. Default: false (opt-in). + """ + git: bool | None = None + """Whether to inject git credentials as an `http..extraheader` so authenticated HTTPS + git works inside the sandbox without the shell-based credential helper the sandbox + blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, + GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's + own helper before the sandbox is applied. Default: false (opt-in). + """ + + @staticmethod + def from_dict(obj: Any) -> 'SandboxConfigAuth': + assert isinstance(obj, dict) + gh = from_union([from_bool, from_none], obj.get("gh")) + git = from_union([from_bool, from_none], obj.get("git")) + return SandboxConfigAuth(gh, git) + + def to_dict(self) -> dict: + result: dict = {} + if self.gh is not None: + result["gh"] = from_union([from_bool, from_none], self.gh) + if self.git is not None: + result["git"] = from_union([from_bool, from_none], self.git) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SandboxConfigUserPolicyExperimentalSeatbelt: @@ -14079,6 +14151,8 @@ class FactoryRunFailure: """Machine-readable factory run failure. Machine-readable failure details for an errored run. + + The run stopped because its usage accounting could not be completed. """ run_id: str """Factory run identifier. @@ -14101,6 +14175,9 @@ class FactoryRunFailure: operation: FactoryDurableOperation | None = None """Execution-critical durable operation that failed.""" + drained_nano_aiu: int | None = None + """Confirmed usage in nano-AIU, representing the floor of what the run spent.""" + @staticmethod def from_dict(obj: Any) -> 'FactoryRunFailure': assert isinstance(obj, dict) @@ -14111,7 +14188,8 @@ def from_dict(obj: Any) -> 'FactoryRunFailure': reason = from_union([from_str, from_none], obj.get("reason")) code = from_union([from_str, from_none], obj.get("code")) operation = from_union([FactoryDurableOperation, from_none], obj.get("operation")) - return FactoryRunFailure(run_id, type, kind, value, reason, code, operation) + drained_nano_aiu = from_union([from_int, from_none], obj.get("drainedNanoAiu")) + return FactoryRunFailure(run_id, type, kind, value, reason, code, operation, drained_nano_aiu) def to_dict(self) -> dict: result: dict = {} @@ -14127,6 +14205,8 @@ def to_dict(self) -> dict: result["code"] = from_union([from_str, from_none], self.code) if self.operation is not None: result["operation"] = from_union([lambda x: to_enum(FactoryDurableOperation, x), from_none], self.operation) + if self.drained_nano_aiu is not None: + result["drainedNanoAiu"] = from_union([from_int, from_none], self.drained_nano_aiu) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -25647,15 +25727,9 @@ class SandboxConfig: Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). """ - gh_auth: bool | None = None - """Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the - OS keyring the sandbox blocks. Default: false (opt-in). - """ - git_auth: bool | None = None - """Whether to inject the Copilot GitHub token as an `http..extraheader` so - authenticated HTTPS git works inside the sandbox without the shell-based credential - helper the sandbox blocks. Default: false (opt-in). - """ + auth: SandboxConfigAuth | None = None + """Credential-injection capability flags.""" + user_policy: SandboxConfigUserPolicy | None = None """User-managed sandbox policy fragment merged into the auto-discovered base policy.""" @@ -25665,10 +25739,9 @@ def from_dict(obj: Any) -> 'SandboxConfig': enabled = from_bool(obj.get("enabled")) add_current_working_directory = from_union([from_bool, from_none], obj.get("addCurrentWorkingDirectory")) allow_dev_tool_access = from_union([from_bool, from_none], obj.get("allowDevToolAccess")) - gh_auth = from_union([from_bool, from_none], obj.get("ghAuth")) - git_auth = from_union([from_bool, from_none], obj.get("gitAuth")) + auth = from_union([SandboxConfigAuth.from_dict, from_none], obj.get("auth")) user_policy = from_union([SandboxConfigUserPolicy.from_dict, from_none], obj.get("userPolicy")) - return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_access, gh_auth, git_auth, user_policy) + return SandboxConfig(enabled, add_current_working_directory, allow_dev_tool_access, auth, user_policy) def to_dict(self) -> dict: result: dict = {} @@ -25677,10 +25750,8 @@ def to_dict(self) -> dict: result["addCurrentWorkingDirectory"] = from_union([from_bool, from_none], self.add_current_working_directory) if self.allow_dev_tool_access is not None: result["allowDevToolAccess"] = from_union([from_bool, from_none], self.allow_dev_tool_access) - if self.gh_auth is not None: - result["ghAuth"] = from_union([from_bool, from_none], self.gh_auth) - if self.git_auth is not None: - result["gitAuth"] = from_union([from_bool, from_none], self.git_auth) + if self.auth is not None: + result["auth"] = from_union([lambda x: to_class(SandboxConfigAuth, x), from_none], self.auth) if self.user_policy is not None: result["userPolicy"] = from_union([lambda x: to_class(SandboxConfigUserPolicy, x), from_none], self.user_policy) return result @@ -25759,19 +25830,42 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class FactoryListRunsResult: - """Factory runs in durable creation order.""" + """A page of factory runs in durable creation order.""" runs: list[FactoryRunSummary] + has_more_newer: bool | None = None + """Whether terminal runs newer than this page exist.""" + + newest_seq: int | None = None + """Newest terminal-run cursor in this page, or null when the terminal window is empty.""" + + oldest_seq: int | None = None + """Oldest terminal-run cursor in this page, or null when the terminal window is empty.""" + + omitted_older: int | None = None + """Number of terminal runs older than this page.""" @staticmethod def from_dict(obj: Any) -> 'FactoryListRunsResult': assert isinstance(obj, dict) runs = from_list(FactoryRunSummary.from_dict, obj.get("runs")) - return FactoryListRunsResult(runs) + has_more_newer = from_union([from_bool, from_none], obj.get("hasMoreNewer")) + newest_seq = from_union([from_int, from_none], obj.get("newestSeq")) + oldest_seq = from_union([from_int, from_none], obj.get("oldestSeq")) + omitted_older = from_union([from_int, from_none], obj.get("omittedOlder")) + return FactoryListRunsResult(runs, has_more_newer, newest_seq, oldest_seq, omitted_older) def to_dict(self) -> dict: result: dict = {} result["runs"] = from_list(lambda x: to_class(FactoryRunSummary, x), self.runs) + if self.has_more_newer is not None: + result["hasMoreNewer"] = from_union([from_bool, from_none], self.has_more_newer) + if self.newest_seq is not None: + result["newestSeq"] = from_union([from_int, from_none], self.newest_seq) + if self.oldest_seq is not None: + result["oldestSeq"] = from_union([from_int, from_none], self.oldest_seq) + if self.omitted_older is not None: + result["omittedOlder"] = from_union([from_int, from_none], self.omitted_older) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -29380,6 +29474,7 @@ class RPC: remote_session_repository: RemoteSessionRepository run_options: RunOptions sandbox_config: SandboxConfig + sandbox_config_auth: SandboxConfigAuth sandbox_config_user_policy: SandboxConfigUserPolicy sandbox_config_user_policy_experimental: SandboxConfigUserPolicyExperimental sandbox_config_user_policy_experimental_seatbelt: SandboxConfigUserPolicyExperimentalSeatbelt @@ -30392,6 +30487,7 @@ def from_dict(obj: Any) -> 'RPC': remote_session_repository = RemoteSessionRepository.from_dict(obj.get("RemoteSessionRepository")) run_options = RunOptions.from_dict(obj.get("RunOptions")) sandbox_config = SandboxConfig.from_dict(obj.get("SandboxConfig")) + sandbox_config_auth = SandboxConfigAuth.from_dict(obj.get("SandboxConfigAuth")) sandbox_config_user_policy = SandboxConfigUserPolicy.from_dict(obj.get("SandboxConfigUserPolicy")) sandbox_config_user_policy_experimental = SandboxConfigUserPolicyExperimental.from_dict(obj.get("SandboxConfigUserPolicyExperimental")) sandbox_config_user_policy_experimental_seatbelt = SandboxConfigUserPolicyExperimentalSeatbelt.from_dict(obj.get("SandboxConfigUserPolicyExperimentalSeatbelt")) @@ -30745,7 +30841,7 @@ def from_dict(obj: Any) -> 'RPC': subagent_settings = from_union([SubagentSettings.from_dict, from_none], obj.get("SubagentSettings")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, disable_bypass_permissions_mode, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, managed_settings_read_result, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_context, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_outcome, permission_decision_reject, permission_decision_request, permission_decision_source, permission_decision_surface, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_all_users, account_get_all_users_result, account_get_current_auth_result, account_get_quota_request, account_get_quota_result, account_login_request, account_login_result, account_logout_request, account_logout_result, account_quota_snapshot, adaptive_thinking_support, agent_discovery_path, agent_discovery_path_list, agent_discovery_path_scope, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_list_request, agent_registry_live_target_entry, agent_registry_live_target_entry_attention_kind, agent_registry_live_target_entry_kind, agent_registry_live_target_entry_last_terminal_event, agent_registry_live_target_entry_status, agent_registry_log_capture, agent_registry_log_capture_open_error_reason, agent_registry_spawn_error, agent_registry_spawn_permission_mode, agent_registry_spawn_registry_timeout, agent_registry_spawn_request, agent_registry_spawn_result, agent_registry_spawn_spawned, agent_registry_spawn_validation_error, agent_registry_spawn_validation_error_field, agent_registry_spawn_validation_error_reason, agent_reload_result, agents_discover_request, agent_select_request, agent_select_result, agent_set_prompt_request, agents_get_discovery_paths_request, allow_all_permission_set_result, allow_all_permission_state, api_key_auth_info, auth_info, auth_info_type, built_in_model_catalog, built_in_model_catalog_entry, cancel_user_requested_shell_command_result, canvas_action, canvas_action_invoke_request, canvas_action_invoke_result, canvas_close_request, canvas_host_context, canvas_host_context_capabilities, canvas_json_schema, canvas_list, canvas_list_open_result, canvas_open_request, canvas_provider_close_request, canvas_provider_invoke_action_request, canvas_provider_open_request, canvas_provider_open_result, canvas_session_context, capi_session_options, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, completions_get_trigger_characters_result, completions_request_request, completions_request_result, configure_session_extensions_params, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_exclusion_check_paths_request, content_exclusion_check_paths_result, content_exclusion_path_check, content_filter_mode, context_heaviest_message, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, current_tool_metadata, debug_collect_logs_collected_entry, debug_collect_logs_destination, debug_collect_logs_entry, debug_collect_logs_entry_kind, debug_collect_logs_include, debug_collect_logs_redaction, debug_collect_logs_request, debug_collect_logs_result, debug_collect_logs_result_kind, debug_collect_logs_skipped_entry, debug_collect_logs_source, disable_bypass_permissions_mode, discovered_canvas, discovered_extension, discovered_extension_mode, discovered_extension_plugin, discovered_extensions, discovered_extensions_disable_request, discovered_extensions_enable_request, discovered_extension_source, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_direction, events_read_result, execute_command_params, execute_command_result, extension, extension_context_push_input, extension_launch_profile, extension_launch_provider_resolve_request, extension_launch_provider_resolve_result, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_shell_exit, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, factory_abort_request, factory_ack_result, factory_agent_options, factory_agent_request, factory_agent_result, factory_agent_summary, factory_cancel_request, factory_current_phase, factory_declared_limits, factory_durable_operation, factory_execute_request, factory_execute_result, factory_get_run_progress_request, factory_get_run_request, factory_journal_get_request, factory_journal_get_result, factory_journal_put_request, factory_list_runs_request, factory_list_runs_result, factory_log_line, factory_log_line_kind, factory_log_request, factory_phase_observation, factory_phase_status, factory_progress_line, factory_progress_page, factory_resume_request, factory_resume_result, factory_run_consumed, factory_run_detail, factory_run_failure, factory_run_failure_kind, factory_run_limits, factory_run_request, factory_run_result, factory_run_status, factory_run_summary, factory_run_terminal, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, git_hub_telemetry_client_info, git_hub_telemetry_event, git_hub_telemetry_notification, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_clear_context_request, history_clear_context_result, history_compact_context_window, history_compact_request, history_compact_result, history_file_restore_skip_reason, history_list_rewind_points_result, history_preview_rewind_request, history_preview_rewind_result, history_rewind_change_type, history_rewind_file_preview, history_rewind_mode, history_rewind_outcome, history_rewind_point, history_rewind_request, history_rewind_result, history_rewind_unavailable_reason, history_skipped_file_restore, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, hook_invoke_request, hook_invoke_response, hook_type, installed_plugin, installed_plugin_info, installed_plugin_source, installed_plugin_source_git_hub, installed_plugin_source_local, installed_plugin_source_url, instruction_discovery_path, instruction_discovery_path_kind, instruction_discovery_path_list, instruction_discovery_path_location, instructions_discover_request, instructions_get_discovery_paths_request, instructions_get_sources_result, instruction_source, instruction_source_location, instruction_source_type, interrupt_main_turn_request, interrupt_main_turn_result, llm_inference_headers, llm_inference_http_request_chunk_request, llm_inference_http_request_chunk_result, llm_inference_http_request_start_request, llm_inference_http_request_start_result, llm_inference_http_request_start_transport, llm_inference_http_response_chunk_error, llm_inference_http_response_chunk_request, llm_inference_http_response_chunk_result, llm_inference_http_response_start_request, llm_inference_http_response_start_result, llm_inference_set_provider_result, local_session_metadata_value, log_request, log_result, lsp_initialize_request, managed_settings_read_result, marketplace_add_result, marketplace_browse_result, marketplace_info, marketplace_list_result, marketplace_plugin_info, marketplace_refresh_entry, marketplace_refresh_result, marketplace_remove_result, mcp_allowed_server, mcp_apps_call_tool_request, mcp_apps_diagnose_capability, mcp_apps_diagnose_request, mcp_apps_diagnose_result, mcp_apps_diagnose_server, mcp_apps_host_context, mcp_apps_host_context_details, mcp_apps_host_context_details_available_display_mode, mcp_apps_host_context_details_display_mode, mcp_apps_host_context_details_platform, mcp_apps_host_context_details_theme, mcp_apps_list_tools_request, mcp_apps_list_tools_result, mcp_apps_read_resource_request, mcp_apps_read_resource_result, mcp_apps_resource_content, mcp_apps_set_host_context_details, mcp_apps_set_host_context_details_available_display_mode, mcp_apps_set_host_context_details_display_mode, mcp_apps_set_host_context_details_platform, mcp_apps_set_host_context_details_theme, mcp_apps_set_host_context_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_configure_git_hub_request, mcp_configure_git_hub_result, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_filtered_server, mcp_headers_handle_pending_headers_refresh_request, mcp_headers_handle_pending_headers_refresh_request_request, mcp_headers_handle_pending_headers_refresh_request_result, mcp_host_state, mcp_is_server_running_request, mcp_is_server_running_result, mcp_list_tools_request, mcp_list_tools_result, mcp_oauth_authentication_state_changed_request, mcp_oauth_handle_pending_request, mcp_oauth_handle_pending_result, mcp_oauth_login_grant_type, mcp_oauth_login_request, mcp_oauth_login_result, mcp_oauth_pending_request_response, mcp_oauth_respond_request, mcp_oauth_respond_result, mcp_register_external_client_request, mcp_reload_with_config_request, mcp_remove_git_hub_result, mcp_resource, mcp_resource_annotations, mcp_resource_content, mcp_resource_icon, mcp_resources_list_request, mcp_resources_list_result, mcp_resources_list_templates_request, mcp_resources_list_templates_result, mcp_resources_read_request, mcp_resources_read_result, mcp_resource_template, mcp_restart_server_request, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_auth_config, mcp_server_auth_config_redirect_port, mcp_server_config, mcp_server_config_defer_tools, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_failure_info, mcp_server_list, mcp_server_needs_auth_info, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, mcp_start_server_request, mcp_start_servers_result, mcp_stop_server_request, mcp_tools, mcp_tool_ui, mcp_tool_ui_visibility, mcp_unregister_external_client_request, memory_configuration, metadata_context_attribution_result, metadata_context_heaviest_messages_request, metadata_context_heaviest_messages_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_promo, model_billing_token_prices, model_billing_token_prices_long_context, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_list_request, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, named_provider_config, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, open_canvas_instance, options_update_additional_content_exclusion_policy, options_update_additional_content_exclusion_policy_rule, options_update_additional_content_exclusion_policy_rule_source, options_update_additional_content_exclusion_policy_scope, options_update_context_tier, options_update_env_value_mode, options_update_reasoning_summary, options_update_tool_filter_precedence, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_factory, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_factory, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_context, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_outcome, permission_decision_reject, permission_decision_request, permission_decision_source, permission_decision_surface, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_allow_all_mode, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_get_allow_all_request, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_factory, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_allow_all_request, permissions_set_allow_all_source, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_read_sql_todos_result, plan_read_sql_todos_with_dependencies_result, plan_sql_todo_dependency, plan_sql_todos_row, plan_update_request, plugin, plugin_install_result, plugin_list, plugin_list_result, plugins_disable_request, plugins_enable_request, plugins_install_request, plugins_marketplaces_add_request, plugins_marketplaces_browse_request, plugins_marketplaces_refresh_request, plugins_marketplaces_remove_request, plugins_reload_request, plugins_uninstall_request, plugins_update_request, plugin_update_all_entry, plugin_update_all_result, plugin_update_result, provider_add_request, provider_add_result, provider_config, provider_config_azure, provider_config_transport, provider_config_type, provider_config_wire_api, provider_endpoint, provider_endpoint_transport, provider_endpoint_type, provider_endpoint_wire_api, provider_get_endpoint_request, provider_model_config, provider_session_token, provider_token_acquire_request, provider_token_acquire_result, push_attachment, push_attachment_blob, push_attachment_directory, push_attachment_file, push_attachment_file_line_range, push_attachment_git_hub_actions_job, push_attachment_git_hub_commit, push_attachment_git_hub_file, push_attachment_git_hub_file_diff, push_attachment_git_hub_file_diff_side, push_attachment_git_hub_reference, push_attachment_git_hub_reference_type, push_attachment_git_hub_release, push_attachment_git_hub_repository, push_attachment_git_hub_snippet, push_attachment_git_hub_tree_comparison, push_attachment_git_hub_tree_comparison_side, push_attachment_git_hub_url, push_attachment_selection, push_attachment_selection_details, push_attachment_selection_details_end, push_attachment_selection_details_start, push_git_hub_repo_ref, queue_begin_deferred_idle_drain_request, queue_begin_deferred_idle_drain_result, queue_consume_system_notifications_request, queued_command_handled, queued_command_not_handled, queued_command_result, queue_defer_session_idle_request, queue_duplicate_at_request, queue_duplicate_at_result, queue_enqueue_resume_pending_result, queue_finish_deferred_idle_drain_request, queue_finish_deferred_idle_drain_result, queue_has_pending_result, queue_insert_at_request, queue_insert_at_result, queue_insert_message, queue_move_item_request, queue_move_item_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_at_request, queue_remove_at_result, queue_remove_most_recent_result, queue_send_now_request, queue_send_now_result, queue_set_drain_paused_request, queue_snapshot_result, queue_update_text_request, queue_update_text_result, register_event_interest_params, register_event_interest_result, register_extension_tools_params, register_extension_tools_result, release_event_interest_params, remote_control_config, remote_control_config_existing_mc_session, remote_control_status, remote_control_status_active, remote_control_status_connecting, remote_control_status_error, remote_control_status_off, remote_control_status_result, remote_control_stop_result, remote_control_transfer_result, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_metadata_repository, remote_session_metadata_task_type, remote_session_metadata_value, remote_session_mode, remote_session_repository, run_options, sandbox_config, sandbox_config_auth, sandbox_config_user_policy, sandbox_config_user_policy_experimental, sandbox_config_user_policy_experimental_seatbelt, sandbox_config_user_policy_filesystem, sandbox_config_user_policy_network, sandbox_config_user_policy_network_proxy, sandbox_config_user_policy_seatbelt, schedule_add_at_request, schedule_add_cron_request, schedule_add_request, schedule_add_result, schedule_add_self_paced_request, schedule_entry, schedule_has_self_paced_result, schedule_list, schedule_rearm_self_paced_request, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachments_to_message_params, send_message_item, send_messages_request, send_messages_result, send_mode, send_request, send_result, send_system_notification_request, server_agent_list, server_instruction_source_list, server_skill, server_skill_list, session_activity, session_agent_list_request, session_auth_status, session_bulk_delete_result, session_cancel_all_background_agents_result, session_capability, session_commands_list_request, session_completion_item, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_sqlite_transaction_error, session_fs_sqlite_transaction_error_class, session_fs_sqlite_transaction_request, session_fs_sqlite_transaction_result, session_fs_sqlite_transaction_statement, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_history_compact_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_git_hub, session_installed_plugin_source_local, session_installed_plugin_source_url, session_limit_prediction_baseline_data, session_limit_prediction_client_type, session_limit_prediction_details, session_limit_prediction_predict_request, session_limit_prediction_request, session_limit_prediction_result, session_limit_prediction_source, session_limit_prediction_tier, session_limit_prediction_tier_option, session_limit_prediction_unavailable_reason, session_list, session_list_entry, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_managed_permissions, session_managed_settings, session_mcp_apps_call_tool_result, session_metadata_snapshot, session_mode, session_model_list, session_model_list_request, session_model_price_category, session_open_options, session_open_options_additional_content_exclusion_policy, session_open_options_additional_content_exclusion_policy_rule, session_open_options_additional_content_exclusion_policy_rule_source, session_open_options_additional_content_exclusion_policy_scope, session_open_options_env_value_mode, session_open_options_reasoning_summary, session_open_params, session_open_result, session_plugins_reload_request, session_provider_get_endpoint_request, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_delete_request, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, session_settings_built_in_tool_availability_snapshot, session_settings_evaluate_predicate_request, session_settings_evaluate_predicate_result, session_settings_job_snapshot, session_settings_model_snapshot, session_settings_online_evaluation_snapshot, session_settings_predicate_name, session_settings_repo_snapshot, session_settings_snapshot, session_settings_validation_snapshot, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_board_entry_count_request, sessions_get_board_entry_count_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_metadata_request, sessions_get_metadata_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_non_empty_session_ids_request, sessions_list_non_empty_session_ids_result, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_open_attach, sessions_open_cloud, sessions_open_create, sessions_open_handoff, sessions_open_handoff_task_type, sessions_open_progress, sessions_open_progress_status, sessions_open_progress_step, sessions_open_remote, sessions_open_resume, sessions_open_resume_last, sessions_open_status, session_source, sessions_prune_old_request, sessions_register_extension_tools_on_session_options, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, sessions_set_remote_control_steering_request, sessions_start_remote_control_request, sessions_stop_remote_control_request, sessions_transfer_remote_control_request, session_telemetry_engagement, session_update_options_params, session_update_options_result, session_visibility_status, session_working_directory_context, session_working_directory_context_host_type, shell_cancel_user_requested_request, shell_exec_request, shell_exec_result, shell_execute_user_requested_request, shell_init_profile, shell_init_script, shell_init_script_shell, shell_kill_request, shell_kill_result, shell_kill_signal, shell_options, shutdown_request, skill, skill_discovery_path, skill_discovery_path_list, skill_discovery_scope, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_discovery_paths_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_choice, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, subagent_settings_entry, subagent_settings_entry_context_tier, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_get_current_metadata_result, tools_initialize_and_validate_result, tools_list_request, tools_update_subagent_settings_result, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_ephemeral_query_request, ui_ephemeral_query_result, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_session_limits_exhausted_request, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_session_limits_exhausted_response, ui_session_limits_exhausted_response_action, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, update_subagent_settings_request, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_requested_shell_command_result, user_setting_metadata, user_settings_get_result, user_settings_set_request, user_settings_set_result, visibility_get_result, visibility_set_request, visibility_set_result, workspace_diff_file_change, workspace_diff_file_change_type, workspace_diff_mode, workspace_diff_result, workspaces_add_summary_request, workspaces_add_summary_result, workspaces_autopilot_objective_exists_result, workspaces_checkpoints, workspaces_create_file_request, workspaces_delete_autopilot_objective_result, workspaces_diff_request, workspaces_ensure_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_autopilot_objective_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspaces_truncate_summaries_request, workspace_summary_host_type, workspaces_update_metadata_request, workspaces_workspace_details_host_type, workspaces_write_autopilot_objective_request, workspaces_write_autopilot_objective_result, session_context_attribution, session_context_info, subagent_settings, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -31404,6 +31500,7 @@ def to_dict(self) -> dict: result["RemoteSessionRepository"] = to_class(RemoteSessionRepository, self.remote_session_repository) result["RunOptions"] = to_class(RunOptions, self.run_options) result["SandboxConfig"] = to_class(SandboxConfig, self.sandbox_config) + result["SandboxConfigAuth"] = to_class(SandboxConfigAuth, self.sandbox_config_auth) result["SandboxConfigUserPolicy"] = to_class(SandboxConfigUserPolicy, self.sandbox_config_user_policy) result["SandboxConfigUserPolicyExperimental"] = to_class(SandboxConfigUserPolicyExperimental, self.sandbox_config_user_policy_experimental) result["SandboxConfigUserPolicyExperimentalSeatbelt"] = to_class(SandboxConfigUserPolicyExperimentalSeatbelt, self.sandbox_config_user_policy_experimental_seatbelt) @@ -32766,9 +32863,11 @@ async def get_run(self, params: FactoryGetRunRequest, *, timeout: float | None = params_dict["sessionId"] = self._session_id return FactoryRunResult.from_dict(await self._client.request("session.factory.getRun", params_dict, **_timeout_kwargs(timeout))) - async def list_runs(self, *, timeout: float | None = None) -> FactoryListRunsResult: - "Lists durable factory runs for this session in creation order.\n\nReturns:\n Factory runs in durable creation order." - return FactoryListRunsResult.from_dict(await self._client.request("session.factory.listRuns", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + async def list_runs(self, params: FactoryListRunsRequest, *, timeout: float | None = None) -> FactoryListRunsResult: + "Lists durable factory runs for this session in creation order.\n\nArgs:\n params: Parameters for paging factory runs.\n\nReturns:\n A page of factory runs in durable creation order." + params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} + params_dict["sessionId"] = self._session_id + return FactoryListRunsResult.from_dict(await self._client.request("session.factory.listRuns", params_dict, **_timeout_kwargs(timeout))) async def get_run_detail(self, params: FactoryGetRunRequest, *, timeout: float | None = None) -> FactoryRunDetail: "Gets durable and live observability detail for one factory run.\n\nArgs:\n params: Parameters for retrieving a factory run.\n\nReturns:\n Full factory run observability detail." @@ -35404,6 +35503,7 @@ async def handle_git_hub_telemetry_event(params: dict) -> None: "RemoteSessionRepository", "RunOptions", "SandboxConfig", + "SandboxConfigAuth", "SandboxConfigUserPolicy", "SandboxConfigUserPolicyExperimental", "SandboxConfigUserPolicyExperimentalSeatbelt", diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index d11317a82..4c3a53e53 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -7836,6 +7836,7 @@ class SubagentCompletedData: agent_display_name: str agent_name: str tool_call_id: str + cancelled: bool | None = None duration: timedelta | None = None model: str | None = None total_tokens: int | None = None @@ -7847,6 +7848,7 @@ def from_dict(obj: Any) -> "SubagentCompletedData": agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) tool_call_id = from_str(obj.get("toolCallId")) + cancelled = from_union([from_none, from_bool], obj.get("cancelled")) duration = from_union([from_none, from_timedelta], obj.get("durationMs")) model = from_union([from_none, from_str], obj.get("model")) total_tokens = from_union([from_none, from_int], obj.get("totalTokens")) @@ -7855,6 +7857,7 @@ def from_dict(obj: Any) -> "SubagentCompletedData": agent_display_name=agent_display_name, agent_name=agent_name, tool_call_id=tool_call_id, + cancelled=cancelled, duration=duration, model=model, total_tokens=total_tokens, @@ -7866,6 +7869,8 @@ def to_dict(self) -> dict: result["agentDisplayName"] = from_str(self.agent_display_name) result["agentName"] = from_str(self.agent_name) result["toolCallId"] = from_str(self.tool_call_id) + if self.cancelled is not None: + result["cancelled"] = from_union([from_none, from_bool], self.cancelled) if self.duration is not None: result["durationMs"] = from_union([from_none, to_timedelta_int], self.duration) if self.model is not None: diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index c93f34cf6..caf9457a8 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -4093,12 +4093,21 @@ pub struct FactoryAckResult {} #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FactoryAgentOptions { + /// Optional custom agent name for the subagent. This field is accepted but not yet honored. + #[serde(skip_serializing_if = "Option::is_none")] + pub agent: Option, + /// Optional context tier for the subagent. This field is accepted but not yet honored. + #[serde(skip_serializing_if = "Option::is_none")] + pub context_tier: Option, /// Optional label distinguishing otherwise identical memoized agent calls. #[serde(skip_serializing_if = "Option::is_none")] pub label: Option, /// Optional model identifier for the subagent. #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, + /// Optional reasoning effort for the subagent. This field is accepted but not yet honored. + #[serde(skip_serializing_if = "Option::is_none")] + pub reasoning_effort: Option, /// Optional JSON Schema for structured agent output. #[serde(skip_serializing_if = "Option::is_none")] pub schema: Option, @@ -4362,7 +4371,7 @@ pub struct FactoryJournalPutRequest { pub run_id: String, } -/// Empty parameters for listing factory runs. +/// Parameters for paging factory runs. /// ///

/// @@ -4372,7 +4381,17 @@ pub struct FactoryJournalPutRequest { ///
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct FactoryListRunsRequest {} +pub struct FactoryListRunsRequest { + /// Exclusive forward cursor. + #[serde(skip_serializing_if = "Option::is_none")] + pub after_seq: Option, + /// Exclusive backward cursor. + #[serde(skip_serializing_if = "Option::is_none")] + pub before_seq: Option, + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, +} /// Durable factory resource consumption. /// @@ -4443,7 +4462,7 @@ pub struct FactoryRunSummary { pub updated_at: i64, } -/// Factory runs in durable creation order. +/// A page of factory runs in durable creation order. /// ///
/// @@ -4454,6 +4473,18 @@ pub struct FactoryRunSummary { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FactoryListRunsResult { + /// Whether terminal runs newer than this page exist. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_more_newer: Option, + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + #[serde(skip_serializing_if = "Option::is_none")] + pub newest_seq: Option, + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + #[serde(skip_serializing_if = "Option::is_none")] + pub oldest_seq: Option, + /// Number of terminal runs older than this page. + #[serde(skip_serializing_if = "Option::is_none")] + pub omitted_older: Option, pub runs: Vec, } @@ -12317,6 +12348,25 @@ pub struct RemoteSessionRepository { pub owner: String, } +/// Credential-injection capability flags applied while the sandbox is enabled. +/// +///
+/// +/// **Experimental.** This type is part of an experimental wire-protocol surface +/// and may change or be removed in future SDK or CLI releases. +/// +///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SandboxConfigAuth { + /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). + #[serde(skip_serializing_if = "Option::is_none")] + pub gh: Option, + /// Whether to inject git credentials as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. github.com is served by the Copilot token; every other forge (Azure DevOps, GitHub Enterprise Server, GitLab, ...) by a credential the host resolves from the user's own helper before the sandbox is applied. Default: false (opt-in). + #[serde(skip_serializing_if = "Option::is_none")] + pub git: Option, +} + /// macOS seatbelt experimental options. /// ///
@@ -12475,14 +12525,11 @@ pub struct SandboxConfig { /// Whether to auto-grant read access to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Default: true (enabled by default; set to false to opt out). #[serde(skip_serializing_if = "Option::is_none")] pub allow_dev_tool_access: Option, + /// Credential-injection capability flags. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option, /// Whether sandboxing is enabled for the session. pub enabled: bool, - /// Whether to export `GH_TOKEN` so the `gh` CLI authenticates inside the sandbox without the OS keyring the sandbox blocks. Default: false (opt-in). - #[serde(skip_serializing_if = "Option::is_none")] - pub gh_auth: Option, - /// Whether to inject the Copilot GitHub token as an `http..extraheader` so authenticated HTTPS git works inside the sandbox without the shell-based credential helper the sandbox blocks. Default: false (opt-in). - #[serde(skip_serializing_if = "Option::is_none")] - pub git_auth: Option, /// User-managed sandbox policy fragment merged into the auto-discovered base policy. #[serde(skip_serializing_if = "Option::is_none")] pub user_policy: Option, @@ -19359,7 +19406,7 @@ pub struct SessionFactoryGetRunResult { pub status: FactoryRunStatus, } -/// Factory runs in durable creation order. +/// A page of factory runs in durable creation order. /// ///
/// @@ -19370,6 +19417,18 @@ pub struct SessionFactoryGetRunResult { #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SessionFactoryListRunsResult { + /// Whether terminal runs newer than this page exist. + #[serde(skip_serializing_if = "Option::is_none")] + pub has_more_newer: Option, + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + #[serde(skip_serializing_if = "Option::is_none")] + pub newest_seq: Option, + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + #[serde(skip_serializing_if = "Option::is_none")] + pub oldest_seq: Option, + /// Number of terminal runs older than this page. + #[serde(skip_serializing_if = "Option::is_none")] + pub omitted_older: Option, pub runs: Vec, } diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index db69aa1e8..fae4d7e16 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -4511,9 +4511,13 @@ impl<'a> SessionRpcFactory<'a> { /// /// Wire method: `session.factory.listRuns`. /// + /// # Parameters + /// + /// * `params` - Parameters for paging factory runs. + /// /// # Returns /// - /// Factory runs in durable creation order. + /// A page of factory runs in durable creation order. /// ///
/// @@ -4522,8 +4526,12 @@ impl<'a> SessionRpcFactory<'a> { /// SDK and CLI versions if your code depends on it. /// ///
- pub async fn list_runs(&self) -> Result { - let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + pub async fn list_runs( + &self, + params: FactoryListRunsRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); let _value = self .session .client() diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index f5bfab83f..913499a6f 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -2860,6 +2860,9 @@ pub struct SubagentCompletedData { pub agent_display_name: String, /// Internal name of the sub-agent pub agent_name: String, + /// Whether the sub-agent was torn down by cancellation - its own abort, or an ancestor being killed - instead of finishing its work. Cancellation is not a failure, so the run still reports completion; this distinguishes a torn-down sub-agent from one that ran to the end. + #[serde(skip_serializing_if = "Option::is_none")] + pub cancelled: Option, /// Wall-clock duration of the sub-agent execution in milliseconds #[serde(skip_serializing_if = "Option::is_none")] pub duration_ms: Option, diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index b55238c17..1a73bb053 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-6.tgz", - "integrity": "sha512-per2cqu8WYuRXXvdU38cYZ7lUSQP5uBDY2QfFZow9FgGOyOToEWz+ykw2NYVKMnx0u1gIiI20Ovl4zec9Dob6w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", + "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-6", - "@github/copilot-darwin-x64": "1.0.79-6", - "@github/copilot-linux-arm64": "1.0.79-6", - "@github/copilot-linux-x64": "1.0.79-6", - "@github/copilot-linuxmusl-arm64": "1.0.79-6", - "@github/copilot-linuxmusl-x64": "1.0.79-6", - "@github/copilot-win32-arm64": "1.0.79-6", - "@github/copilot-win32-x64": "1.0.79-6" + "@github/copilot-darwin-arm64": "1.0.79-9", + "@github/copilot-darwin-x64": "1.0.79-9", + "@github/copilot-linux-arm64": "1.0.79-9", + "@github/copilot-linux-x64": "1.0.79-9", + "@github/copilot-linuxmusl-arm64": "1.0.79-9", + "@github/copilot-linuxmusl-x64": "1.0.79-9", + "@github/copilot-win32-arm64": "1.0.79-9", + "@github/copilot-win32-x64": "1.0.79-9" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-6.tgz", - "integrity": "sha512-22aYilTJsiZX4w55DPXHvJFHSNwZWGip4DcQCQTvzzVIGc8MjlCQVModE9B6ElGs18hUaM3NH4piTYYT0HqNGQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", + "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-6.tgz", - "integrity": "sha512-1ESqmLenOGkfD4KwgxtUZh+Wt5+qKwtLHGpfpRl+d/BSKj4cNo9FUO2vFEmF3zQefpmady3vmDURSdi65hlC+w==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", + "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-6.tgz", - "integrity": "sha512-R8ZmfoJuOj1CT0zamAnRJi7nxhUPRFi3vo3dWlzSkto0Uwez+j2IJmyGIZEZbN65BJJJMD2/kg0GZQG+l+PmUA==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", + "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-6.tgz", - "integrity": "sha512-P8Dgq59MIoiWKTRUGLrzzQ+NX54sqsHQFAoTJPis2K0N1O7BUTN4RDl8aPN3v4c1MCkbH9YzjmT8ns1JPeIUuQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", + "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-6.tgz", - "integrity": "sha512-6CS4YuL1x8YwoEfr/dPcq+ZQYTJQTukO/Uuv88eZ9/RWGdhcls14j/WWPcte8LLk2EFJXchM9WPmkOmZppPkwQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", + "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-6.tgz", - "integrity": "sha512-y+fX6P4oXKADqXsEWCTqWFmLECTm2jVmxkCEC6C1TGqHDzN0+X2pJQd/LTSOZFmtlgxVjusL93eCk1mwa2MapQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", + "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-6.tgz", - "integrity": "sha512-E/JxBAA4Dqy7d81mCBfZ1L9XJH7eK1DBQn2Jlur5oBvA3qluX05kXcGTlmGkGVA2mkM9rD5zaSC8yZ5grq40HQ==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", + "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-6", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-6.tgz", - "integrity": "sha512-7Hlfb438QNqU34OhhRiJiElWoyP7xED5iZunU1vC00L1RbrsTXDeC41y2oAxYiXa/bX60TV4x/oWuGli/0no6A==", + "version": "1.0.79-9", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", + "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index efb64a56d..3b9d8e289 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.79-6", + "@github/copilot": "^1.0.79-9", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From 3108e8ce26286043afa52f12781331460628baa0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:14:53 -0400 Subject: [PATCH 51/72] Update @github/copilot to 1.0.79 (#2306) * Update @github/copilot to 1.0.79 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code * Fix flaky Node cleanup test Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Stephen Toub Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +++++++++++++------------- java/scripts/codegen/package.json | 2 +- nodejs/package-lock.json | 72 +++++++++++++------------- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/test/e2e/client.e2e.test.ts | 9 +++- test/harness/package-lock.json | 72 +++++++++++++------------- test/harness/package.json | 2 +- 9 files changed, 120 insertions(+), 115 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index c5e9f62ad..796051891 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -88,7 +88,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.79-9 + ^1.0.79 diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index d4824feba..527eef03e 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", - "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", + "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-9", - "@github/copilot-darwin-x64": "1.0.79-9", - "@github/copilot-linux-arm64": "1.0.79-9", - "@github/copilot-linux-x64": "1.0.79-9", - "@github/copilot-linuxmusl-arm64": "1.0.79-9", - "@github/copilot-linuxmusl-x64": "1.0.79-9", - "@github/copilot-win32-arm64": "1.0.79-9", - "@github/copilot-win32-x64": "1.0.79-9" + "@github/copilot-darwin-arm64": "1.0.79", + "@github/copilot-darwin-x64": "1.0.79", + "@github/copilot-linux-arm64": "1.0.79", + "@github/copilot-linux-x64": "1.0.79", + "@github/copilot-linuxmusl-arm64": "1.0.79", + "@github/copilot-linuxmusl-x64": "1.0.79", + "@github/copilot-win32-arm64": "1.0.79", + "@github/copilot-win32-x64": "1.0.79" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", - "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", + "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", - "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", + "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", - "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", + "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", - "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", + "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", - "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", + "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", - "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", + "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", - "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", + "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", - "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", + "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index 5e3630ec0..b1393f40b 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 39fa8b6fa..0fe7660f8 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", - "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", + "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-9", - "@github/copilot-darwin-x64": "1.0.79-9", - "@github/copilot-linux-arm64": "1.0.79-9", - "@github/copilot-linux-x64": "1.0.79-9", - "@github/copilot-linuxmusl-arm64": "1.0.79-9", - "@github/copilot-linuxmusl-x64": "1.0.79-9", - "@github/copilot-win32-arm64": "1.0.79-9", - "@github/copilot-win32-x64": "1.0.79-9" + "@github/copilot-darwin-arm64": "1.0.79", + "@github/copilot-darwin-x64": "1.0.79", + "@github/copilot-linux-arm64": "1.0.79", + "@github/copilot-linux-x64": "1.0.79", + "@github/copilot-linuxmusl-arm64": "1.0.79", + "@github/copilot-linuxmusl-x64": "1.0.79", + "@github/copilot-win32-arm64": "1.0.79", + "@github/copilot-win32-x64": "1.0.79" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", - "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", + "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", - "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", + "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", - "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", + "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", - "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", + "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", - "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", + "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", - "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", + "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", - "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", + "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", - "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", + "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index a4160cd2c..437a77a01 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index ad8403002..66b4df470 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/test/e2e/client.e2e.test.ts b/nodejs/test/e2e/client.e2e.test.ts index 89489f78e..35e744076 100644 --- a/nodejs/test/e2e/client.e2e.test.ts +++ b/nodejs/test/e2e/client.e2e.test.ts @@ -1,5 +1,5 @@ import { ChildProcess } from "child_process"; -import { describe, expect, it, onTestFinished } from "vitest"; +import { describe, expect, it, onTestFinished, vi } from "vitest"; import { approveAll, CopilotClient, RuntimeConnection } from "../../src/index.js"; import { isInProcessTransport } from "./harness/sdkTestContext.js"; @@ -95,7 +95,12 @@ describe("Client", () => { const cliProcess = (client as any).cliProcess as ChildProcess; expect(cliProcess).toBeDefined(); cliProcess.kill("SIGKILL"); - await new Promise((resolve) => setTimeout(resolve, 100)); + await vi.waitFor( + () => { + expect((client as unknown as { state: string }).state).toBe("disconnected"); + }, + { timeout: 10_000 } + ); const errors = await client.stop(); if (errors.length > 0) { diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 1a73bb053..17a839f0d 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79-9.tgz", - "integrity": "sha512-1QRRV3z1HA8sr3JUo4e2l+zFVbIGIOqnmTwzdJ+ilfr+mkmC7LYWkJxV3+j+zN0nq8JAgusSL/Schc43MPIqug==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", + "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79-9", - "@github/copilot-darwin-x64": "1.0.79-9", - "@github/copilot-linux-arm64": "1.0.79-9", - "@github/copilot-linux-x64": "1.0.79-9", - "@github/copilot-linuxmusl-arm64": "1.0.79-9", - "@github/copilot-linuxmusl-x64": "1.0.79-9", - "@github/copilot-win32-arm64": "1.0.79-9", - "@github/copilot-win32-x64": "1.0.79-9" + "@github/copilot-darwin-arm64": "1.0.79", + "@github/copilot-darwin-x64": "1.0.79", + "@github/copilot-linux-arm64": "1.0.79", + "@github/copilot-linux-x64": "1.0.79", + "@github/copilot-linuxmusl-arm64": "1.0.79", + "@github/copilot-linuxmusl-x64": "1.0.79", + "@github/copilot-win32-arm64": "1.0.79", + "@github/copilot-win32-x64": "1.0.79" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79-9.tgz", - "integrity": "sha512-DBTtv06Lpka1R+bkYfCWH38838b66bTLvsRsNjSuQV9AzgMPHjIn+Q2wAmU89rUyw5ej9iCIL5ppps+roi7qRw==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", + "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79-9.tgz", - "integrity": "sha512-j+XOqtVWa0EZ8lBR+xIsPVI7ErpDepgr3WuSfUCkC9fOdvnVJ1rbyBY3gCM/urY4wJNdbHc4t1vN9MjtRNpTGA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", + "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79-9.tgz", - "integrity": "sha512-eYsJlokogYeXo2tCwqoDW+rELbLs0ht7b3R1nrl9odO6U6ZabIm6BilSLSxjWCtE1ZpqDLmdJbOV0/6cQwjvlA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", + "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79-9.tgz", - "integrity": "sha512-KknlE4hT3rw/Ne1aL+F1KEQrq40d4I8aGfxAEuahmn3NaM7CSBikDVIsAvkmTq7e2vHzaLytpclc8+gcEAIK6Q==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", + "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79-9.tgz", - "integrity": "sha512-Wrk+vpzg9ho/uC7ajNgIEjPShtB+pGlaJV+8rvYcHeQG6Gyx8YrPMI+OMdYou6RI9firqIaywT0UPuy9PUzoOg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", + "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79-9.tgz", - "integrity": "sha512-ERULakTfCb4KYK4hGcMPRp5qm5K7KqKFHhGrAdn1VLrm8QsXDYzl2vUO4tIU/TxRW5VA9pmWh30JpCjQScSnDA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", + "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79-9.tgz", - "integrity": "sha512-XAe2calnhhiAVdIb2jUn4Bh961nu4zz3N5tcrb5CNjDSbjEE2LQf8CaNV4h/QhHjjpcmqFIfMQBMYxW1+9xixg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", + "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79-9", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79-9.tgz", - "integrity": "sha512-/nK1IR5Vho2r6vuzq9xziVWwlgan41eL62Oo0feu7RaMvhS+f40l4jZCjuWLbeysh/we642qCyRjK8gkv730lA==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", + "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 3b9d8e289..7903a8f4e 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.79-9", + "@github/copilot": "^1.0.79", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From e2cd7adbdc617c665d5fd67d08ebb6f69f280d29 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 11 Aug 2026 10:31:19 -0400 Subject: [PATCH 52/72] Consolidate SDK GitHub releases (#2305) * Consolidate SDK GitHub releases Create one shared GitHub Release for all SDK languages while retaining scoped Rust and Java source tags. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 01a9d0e3-0073-48f2-afae-de0398986669 * Gate releases on Maven publication Expose Maven publication success from the reusable Java workflow so documentation deployment failures do not suppress the shared SDK release. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 01a9d0e3-0073-48f2-afae-de0398986669 * Clarify unified release guidance Document the Node-only unstable channel and align changelog examples with the six-language release policy. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 01a9d0e3-0073-48f2-afae-de0398986669 --------- Copilot-Session: 01a9d0e3-0073-48f2-afae-de0398986669 --- .github/workflows/java-publish-maven.yml | 100 ++----------------- .github/workflows/java.notes.template | 29 ------ .github/workflows/publish.yml | 32 +----- .github/workflows/release-changelog.lock.yml | 4 +- .github/workflows/release-changelog.md | 29 +++--- docs/developer-docs/secrets.md | 2 +- rust/README.md | 2 +- rust/RELEASING.md | 35 +++---- 8 files changed, 44 insertions(+), 189 deletions(-) delete mode 100644 .github/workflows/java.notes.template diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml index e293d9127..f80b38f76 100644 --- a/.github/workflows/java-publish-maven.yml +++ b/.github/workflows/java-publish-maven.yml @@ -37,6 +37,10 @@ on: type: boolean required: false default: false + outputs: + mavenPublished: + description: "Whether the Java package was published to Maven Central" + value: ${{ jobs.publish-maven.outputs.published }} secrets: JAVA_RELEASE_TOKEN: required: true @@ -80,39 +84,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_TOKEN }} - - name: Verify JAVA_RELEASE_GITHUB_TOKEN can trigger workflows - run: | - # JAVA_RELEASE_GITHUB_TOKEN is used for: - # - gh workflow run release-changelog.lock.yml (requires actions:write) - # Check the token's OAuth scopes for 'workflow' (classic PAT) or - # attempt a workflow dispatch with a non-existent ref to verify write access - # (fine-grained PAT — these don't expose scopes via X-OAuth-Scopes). - SCOPES=$(gh api -i user 2>&1 | grep -i '^x-oauth-scopes:' | tr '[:upper:]' '[:lower:]' || true) - if echo "$SCOPES" | grep -q 'workflow'; then - echo "JAVA_RELEASE_GITHUB_TOKEN has 'workflow' scope (classic PAT)" - elif [ -z "$SCOPES" ]; then - # Fine-grained PAT: no X-OAuth-Scopes header returned. - # Attempt a workflow dispatch against a non-existent ref. If the token - # has actions:write, the API returns 422 (validation failed on ref). - # If it lacks the permission, the API returns 403. - HTTP_CODE=$(gh api -X POST \ - "repos/${{ github.repository }}/actions/workflows/release-changelog.lock.yml/dispatches" \ - -f ref="preflight-check-nonexistent-ref" \ - -f 'inputs[tag]=preflight-check' \ - --silent -i 2>&1 | head -1 | grep -oE '[0-9]{3}' || echo "000") - if [ "$HTTP_CODE" = "403" ] || [ "$HTTP_CODE" = "000" ]; then - echo "::error::JAVA_RELEASE_GITHUB_TOKEN lacks actions:write permission on ${{ github.repository }}. It cannot trigger the changelog generation workflow." - exit 1 - fi - # 422 = has write access but ref doesn't exist (expected), 204 would mean it dispatched (shouldn't happen with fake ref) - echo "JAVA_RELEASE_GITHUB_TOKEN actions:write access OK (fine-grained PAT, dispatch returned HTTP ${HTTP_CODE})" - else - echo "::error::JAVA_RELEASE_GITHUB_TOKEN lacks 'workflow' scope. Found scopes: ${SCOPES}. It needs this scope to trigger changelog generation via gh workflow run." - exit 1 - fi - env: - GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_GITHUB_TOKEN }} - publish-maven: name: Publish Java SDK to Maven Central needs: preflight @@ -123,6 +94,7 @@ jobs: working-directory: ./java outputs: version: ${{ steps.versions.outputs.release_version }} + published: ${{ steps.publish-maven.outcome == 'success' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -227,6 +199,7 @@ jobs: JAVA_GPG_PASSPHRASE: ${{ secrets.JAVA_GPG_PASSPHRASE }} - name: Perform Release and Deploy to Maven Central + id: publish-maven working-directory: ./java run: | mvn -B release:perform \ @@ -248,68 +221,9 @@ jobs: # Also run Maven release:rollback to clean up any partial release state mvn -B release:rollback || true - github-release: - name: Create GitHub Release - needs: [preflight, publish-maven] - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - defaults: - run: - shell: bash - working-directory: ./java - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - fetch-depth: 0 - - name: Create GitHub Release - run: | - VERSION="${{ needs.publish-maven.outputs.version }}" - GROUP_ID="com.github" - ARTIFACT_ID="copilot-sdk-java" - CURRENT_TAG="java/v${VERSION}" - - if gh release view "${CURRENT_TAG}" >/dev/null 2>&1; then - echo "Release ${CURRENT_TAG} already exists. Skipping creation." - exit 0 - fi - - # Generate release notes from template - export VERSION GROUP_ID ARTIFACT_ID - RELEASE_NOTES=$(envsubst < $GITHUB_WORKSPACE/.github/workflows/java.notes.template) - - # Get the previous tag for generating notes - # grep returns exit 1 when no lines match (first release), so - # append "|| true" to prevent pipefail from aborting the script. - PREV_TAG=$(git tag --list 'java/v*' --sort=-version:refname \ - | grep -Fxv "${CURRENT_TAG}" \ - | head -n 1 || true) - - echo "Current tag: ${CURRENT_TAG}" - echo "Previous tag: ${PREV_TAG}" - - # Build the gh release command - GH_ARGS=("${CURRENT_TAG}") - GH_ARGS+=("--title" "GitHub Copilot SDK for Java ${VERSION}") - GH_ARGS+=("--notes" "${RELEASE_NOTES}") - GH_ARGS+=("--generate-notes") - - if [ -n "$PREV_TAG" ]; then - GH_ARGS+=("--notes-start-tag" "$PREV_TAG") - fi - - ${{ inputs.prerelease == true && 'GH_ARGS+=("--prerelease")' || '' }} - - gh release create "${GH_ARGS[@]}" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Trigger changelog generation - run: gh workflow run release-changelog.lock.yml -f tag="java/v${{ needs.publish-maven.outputs.version }}" - env: - GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_GITHUB_TOKEN }} - deploy-site: name: Deploy Documentation Site - needs: [preflight, publish-maven, github-release] + needs: [preflight, publish-maven] if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/java.notes.template b/.github/workflows/java.notes.template deleted file mode 100644 index e209a110b..000000000 --- a/.github/workflows/java.notes.template +++ /dev/null @@ -1,29 +0,0 @@ - - -# Installation - -⚠️ **Artifact versioning plan:** Releases of this implementation track releases of the reference implementation. For each release of the reference implementation, there may follow a corresponding release of this implementation with the same number as the reference implementation. Release identifiers of the reference implementation are in the form `vMaj.Min.Micro`. For example v0.1.32. The corresponding maven version for the release will be `Maj.Min.Micro-java.N`, where `Maj`, `Min` and `Micro` are the corresponding numbers for the reference implementation release, and `N` is a monotonically increasing sequence number starting with 0 for each release. See the corresponding architectural decision record for more information in the `docs/adr` directory of the source code. - -📦 [View on Maven Central](https://central.sonatype.com/artifact/${GROUP_ID}/${ARTIFACT_ID}/${VERSION}) - -📖 [Documentation](https://github.github.io/copilot-sdk-java/${VERSION}/) · [Javadoc](https://github.github.io/copilot-sdk-java/${VERSION}/apidocs/index.html) - - -## Maven -```xml - - ${GROUP_ID} - ${ARTIFACT_ID} - ${VERSION} - -``` - -## Gradle (Kotlin DSL) -```kotlin -implementation("${GROUP_ID}:${ARTIFACT_ID}:${VERSION}") -``` - -## Gradle (Groovy DSL) -```groovy -implementation '${GROUP_ID}:${ARTIFACT_ID}:${VERSION}' -``` diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b44fd582a..98bf23690 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -379,7 +379,8 @@ jobs: needs.publish-nodejs.result == 'success' && needs.publish-dotnet.result == 'success' && needs.publish-python.result == 'success' && - needs.publish-rust.result == 'success' + needs.publish-rust.result == 'success' && + needs.publish-java.outputs.mavenPublished == 'true' runs-on: ubuntu-latest permissions: actions: write @@ -434,11 +435,9 @@ jobs: fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Tag Rust SDK and create Rust GitHub Release - # Rust gets its own version-scoped GitHub Release with notes - # derived from PR titles since the previous Rust tag. The - # cross-language `vX.Y.Z` release above still exists; this one - # is the canonical reference for Rust users. + - name: Tag Rust SDK + # Keep a language-scoped source tag for traceability. Rust is + # included in the cross-language `vX.Y.Z` GitHub Release. if: github.event.inputs.dist-tag == 'latest' || github.event.inputs.dist-tag == 'prerelease' run: | set -e @@ -453,26 +452,5 @@ jobs: else echo "Tag $TAG_NAME already exists, skipping tag push" fi - # Find the previous Rust tag for note generation. Prefer rust/v*, - # fall back to the historical rust-v* tags from the release-plz era. - PREV_TAG=$(git tag --list 'rust/v*' --sort=-v:refname | grep -vFx "$TAG_NAME" | head -n1) - if [ -z "$PREV_TAG" ]; then - PREV_TAG=$(git tag --list 'rust-v*' --sort=-v:refname | head -n1) - fi - NOTES_FLAG="" - if [ -n "$PREV_TAG" ]; then - NOTES_FLAG="--notes-start-tag $PREV_TAG" - echo "Generating notes from $PREV_TAG..$TAG_NAME" - else - echo "No previous Rust tag found; generating notes from full history" - fi - PRERELEASE_FLAG="" - if [ "${{ github.event.inputs.dist-tag }}" = "prerelease" ]; then - PRERELEASE_FLAG="--prerelease" - fi - gh release create "$TAG_NAME" \ - --title "$TAG_NAME" \ - --generate-notes $NOTES_FLAG $PRERELEASE_FLAG \ - --target ${{ github.sha }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-changelog.lock.yml b/.github/workflows/release-changelog.lock.yml index 781f41b22..23b19d9b8 100644 --- a/.github/workflows/release-changelog.lock.yml +++ b/.github/workflows/release-changelog.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"9342b428009e6a3b47258c08b78735a89fc72714b73a44b72c4714e310d60006","body_hash":"89e26ed929f440bd6af57d1da92b06dbf1739b4a1d34b9923286919d00f272d1","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a4a0859e0103be270433c7fe1926a346c46271b4b530b2c08fa5d606d0ab75c4","body_hash":"490b25b529910b1b087df624fd59eaef52e142e84b9503ca1cf87631f4c36b53","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"8bdba8075360648fe6802302a5b4e016361dc6ac","version":"v0.83.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38","digest":"sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38@sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38","digest":"sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38@sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38","digest":"sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38@sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.3","digest":"sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.3@sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw (v0.83.1). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -62,7 +62,7 @@ on: required: false type: string tag: - description: Release tag to generate changelog for (e.g., v0.1.30, /v1.0.0) + description: Release tag to generate changelog for (e.g., v1.0.0) required: true type: string diff --git a/.github/workflows/release-changelog.md b/.github/workflows/release-changelog.md index 7a682c56d..c846b0dd3 100644 --- a/.github/workflows/release-changelog.md +++ b/.github/workflows/release-changelog.md @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: tag: - description: "Release tag to generate changelog for (e.g., v0.1.30, /v1.0.0)" + description: "Release tag to generate changelog for (e.g., v1.0.0)" required: true type: string permissions: @@ -55,9 +55,8 @@ Use the GitHub API to fetch the release corresponding to `${{ github.event.input 2. The **new version** is the release tag: `${{ github.event.inputs.tag }}` 3. Fetch the release metadata to determine if this is a **stable** or **prerelease** release. 4. Determine the **previous version** to diff against: - - **Scoped tags**: If the tag has a language prefix (e.g., `java/v1.0.0` or `rust/v0.2.0`), the previous tag must use the **same prefix**. List tags matching that prefix (e.g., `java/v*` or `rust/v*`) sorted by version and pick the one immediately before the current tag. Only compare within the same scope. - - **For stable releases**: find the previous **stable** release (skip prereleases). Check `CHANGELOG.md` for the most recent version heading matching this scope (`## [vX.Y.Z](...)` for unscoped, `## [java/vX.Y.Z](...)` for Java, `## [rust/vX.Y.Z](...)` for Rust), or fall back to listing releases via the API. This means stable changelogs include ALL changes since the last stable release, even if some were already mentioned in prerelease notes. - - **For prerelease releases**: find the most recent release of **any kind** (stable or prerelease) that precedes this one within the same tag scope. This way prerelease notes only cover what's new since the last release. + - **For stable releases**: find the previous **stable** release (skip prereleases). Check `CHANGELOG.md` for the most recent `## [vX.Y.Z](...)` heading, or fall back to listing releases via the API. This means stable changelogs include ALL changes since the last stable release, even if some were already mentioned in prerelease notes. + - **For prerelease releases**: find the most recent release of **any kind** (stable or prerelease) that precedes this one. This way prerelease notes only cover what's new since the last release. 5. If no previous release exists at all, use the first commit in the repo as the starting point. 6. After identifying the range, verify it by listing the commits in `PREVIOUS_TAG..NEW_TAG`. If the local result still looks suspiciously small or inconsistent, do **not** proceed based on local git alone — use the GitHub tools as the source of truth for the commits and PRs in the release. @@ -68,8 +67,7 @@ Use the GitHub API to fetch the release corresponding to `${{ github.event.input - PR number and title - The PR author - Which SDK(s) were affected (look for prefixes like `[C#]`, `[Python]`, `[Go]`, `[Node]`, `[Java]`, `[Rust]` in the title, or infer from changed files) -3. **For scoped tags** (e.g., `java/v*`, `rust/v*`): only include changes that touch the corresponding language directory (`java/`, `rust/`). Ignore changes to other languages unless they directly affect the scoped SDK. -4. Ignore: +3. Ignore: - Dependabot/bot PRs that only bump internal dependencies (like `Update @github/copilot to ...`) unless they bring user-facing changes - Merge commits with no meaningful content - Preview/prerelease-only changes that were already documented @@ -81,6 +79,10 @@ Separate the changes into two groups: 1. **Highlighted features**: Any interesting new feature or significant improvement that deserves its own section with a description and code snippet(s). Read the PR diff and source code to understand the feature well enough to write about it. 2. **Other changes**: Bug fixes, minor improvements, and smaller features that can be summarized in a single bullet each. +**Format for each highlighted feature** — use an `### Feature:` or `### Fix:` heading, a 1-2 sentence description explaining what it does and why it matters, and at least one short code snippet (max 3 lines). Cover all six SDKs—TypeScript, C#, Go, Python, Java, and Rust—in the combined release notes. Show code examples in the languages whose APIs best illustrate the change, and ensure every user-visible language-specific change appears either as a highlighted feature or under other changes. + +**Format for other changes** — use a single `### Other changes` section with a flat bulleted list. Each bullet has a lowercase prefix (`feature:`, `bugfix:`, `improvement:`) and a one-line description linking to the PR. **However, if there are no highlighted features above it, omit the `### Other changes` heading.** + Only include changes that are **user-visible in the published SDK packages**. Skip anything that only affects docs, CI, build tooling, GitHub workflows, test infrastructure, or other internal-only concerns. Additionally, identify **new contributors** — anyone whose first merged PR to this repo falls within this release range. You can determine this by checking whether the author has any earlier merged PRs in the repository. @@ -90,11 +92,7 @@ Additionally, identify **new contributors** — anyone whose first merged PR to **Skip this step entirely for prerelease releases.** 1. Read the current `CHANGELOG.md` file. -2. Add the new version entry **at the top** of the file, right after the title/header. Use the **full tag** as the version in the heading — e.g., `## [v0.2.3](...)` for unscoped tags, `## [java/v1.0.0](...)` for Java-scoped tags, `## [rust/v0.2.3](...)` for Rust-scoped tags. - -**Format for each highlighted feature** — use an `### Feature:` or `### Fix:` heading, a 1-2 sentence description explaining what it does and why it matters, and at least one short code snippet (max 3 lines). For unscoped releases, focus on **TypeScript** and **C#** as the primary languages; only show Go/Python when giving a list of one-liner equivalents across all languages, or when their usage pattern is meaningfully different. For **scoped releases** (e.g., `java/v*`), show code snippets in the scoped language only (e.g., Java for `java/v*`, Rust for `rust/v*`). - -**Format for other changes** — a single `### Other changes` section with a flat bulleted list. Each bullet has a lowercase prefix (`feature:`, `bugfix:`, `improvement:`) and a one-line description linking to the PR. **However, if there are no highlighted features above it, omit the `### Other changes` heading entirely** — just list the bullets directly under the version heading. +2. Add the new version entry **at the top** of the file, right after the title/header. Use the full tag as the version in the heading, for example `## [v1.0.0](...)`. 3. Use the release's publish date (from the GitHub Release metadata), not today's date. For `workflow_dispatch` runs, fetch the release by tag to get the date. 4. If there are new contributors, add a `### New contributors` section at the end listing each with a link to their first PR: @@ -118,11 +116,6 @@ Use the `create-pull-request` output to submit your changes. The PR should: Use the `update-release` output to replace the auto-generated release notes with your nicely formatted changelog. **Do not include the version heading** (`## [vX.Y.Z](...) (date)`) in the release notes — the release already has a title showing the version. Start directly with the feature sections or other changes list. -**IMPORTANT — Preserving the Installation section:** -The release body may contain an Installation section delimited by `` and `` HTML comments. In the case of Java, this section includes Maven/Gradle dependency snippets and a "View on Maven Central" link. You **MUST** preserve this entire section (from the opening comment through the closing comment, inclusive) exactly as it appears in the existing release body. Place your generated changelog content **after** the Installation section. - -**URL reconstruction:** If the Maven Central URL in the Installation section appears corrupted or contains the word "redacted", reconstruct it. Extract the version from the release tag (e.g., `java/v1.0.0` → `1.0.0`), and rebuild the URL as: `https://central.sonatype.com/artifact/com.github/copilot-sdk-java/{VERSION}`. The `` HTML comment in the section contains the intended URL pattern. - ## Example Output Here is an example of what a changelog entry should look like, based on real commits from this repo. **Follow this style exactly.** @@ -154,6 +147,8 @@ While `session.rpc.models.setModel()` already worked, there is now a convenience - C#: `session.SetModel("gpt-4o")` - Python: `session.set_model("gpt-4o")` - Go: `session.SetModel("gpt-4o")` +- Java: `session.setModel("gpt-4o").get()` +- Rust: `session.set_model("gpt-4o", None).await?` ### Other changes @@ -171,7 +166,7 @@ While `session.rpc.models.setModel()` already worked, there is now a convenience **Key rules visible in the example:** - Highlighted features get their own `### Feature:` heading, a short description, and code snippets -- Code snippets are TypeScript and C# primarily; Go/Python only when listing one-liner equivalents or when meaningfully different +- Code snippets use whichever of TypeScript, C#, Go, Python, Java, and Rust best illustrate the change; list all affected languages when showing equivalents - The `### Other changes` section is a flat bulleted list with lowercase `bugfix:` / `feature:` / `improvement:` prefixes - PR numbers are linked inline, not at the end with author attribution (keep it clean) diff --git a/docs/developer-docs/secrets.md b/docs/developer-docs/secrets.md index 15788bbe5..573f4f22e 100644 --- a/docs/developer-docs/secrets.md +++ b/docs/developer-docs/secrets.md @@ -50,7 +50,7 @@ These secrets are used by the Java SDK Maven Central publishing workflow (`java- * **`JAVA_RELEASE_TOKEN`**: GitHub token with **push** permission on the repository. Used by the release workflow for `actions/checkout`, pushing release commits and tags to `main`, and running `mvn release:prepare -DpushChanges=true`. * Workflows: `java-publish-maven.yml` -* **`JAVA_RELEASE_GITHUB_TOKEN`**: GitHub token with **workflow dispatch** (actions:write) permission on this repository and `github/copilot-sdk-java`. Used to trigger the `release-changelog.lock.yml` workflow and the documentation site deployment after a release is published. +* **`JAVA_RELEASE_GITHUB_TOKEN`**: GitHub token with **workflow dispatch** (actions:write) permission on `github/copilot-sdk-java`. Used to trigger the documentation site deployment after a release is published. * Workflows: `java-publish-maven.yml` ## Rust publishing secret diff --git a/rust/README.md b/rust/README.md index 314090044..29fe67355 100644 --- a/rust/README.md +++ b/rust/README.md @@ -4,7 +4,7 @@ A Rust SDK for programmatic access to the GitHub Copilot CLI. See [github/copilot-sdk](https://github.com/github/copilot-sdk) for the equivalent SDKs in TypeScript, Python, Go, .NET, and Java. The Rust SDK seeks parity with those SDKs; see [Differences From Other SDKs](#differences-from-other-sdks) below for the small set of intentional divergences. -**Releases:** [github.com/github/copilot-sdk/releases?q=rust%2F](https://github.com/github/copilot-sdk/releases?q=rust%2F) — per-version release notes for the Rust crate. +**Releases:** [github.com/github/copilot-sdk/releases](https://github.com/github/copilot-sdk/releases) — combined release notes for all SDK languages. ## Prerequisites diff --git a/rust/RELEASING.md b/rust/RELEASING.md index de0252de8..06e362f54 100644 --- a/rust/RELEASING.md +++ b/rust/RELEASING.md @@ -1,8 +1,7 @@ # Releasing `github-copilot-sdk` -The Rust crate ships through the same unified `publish.yml` workflow -as the Node, .NET, and Python SDKs. There is no Rust-specific release -workflow. +The Rust crate ships through the unified `publish.yml` workflow +alongside the other SDKs. There is no Rust-specific release workflow. ## TL;DR @@ -16,9 +15,11 @@ workflow. prerelease version requirement to install it. - `unstable` — skipped for Rust (Cargo doesn't have a clean equivalent of npm's `unstable` dist-tag). -4. The workflow publishes all four SDKs at the shared computed - version, tags `rust/vX.Y.Z`, and creates a Rust-scoped GitHub - Release with auto-generated notes since the previous Rust tag. +4. For `latest` and `prerelease`, the workflow publishes all SDKs at + the shared computed version, tags `rust/vX.Y.Z` for source + traceability, and creates one combined `vX.Y.Z` GitHub Release. + The `unstable` channel publishes only the Node.js SDK and does not + create a GitHub Release. ## Version, tag, and release notes @@ -26,14 +27,11 @@ workflow. as a placeholder. CI overrides it at publish time with the version computed by `publish.yml` (or an explicit `version` workflow input). - **Tag:** `rust/vX.Y.Z` (matches the `go/vX.Y.Z` style used elsewhere - in this repo). The historical `rust-v0.1.0` tag from the - release-plz era stays valid as a starting point for auto-generated - release notes. -- **Release notes:** auto-generated by `gh release --generate-notes` - from PR titles between the previous Rust tag and the new one. - Write descriptive PR titles for any change that touches the Rust - surface; that's the only place those changes will be visible to - Rust users. + in this repo). The tag identifies the source used for that crate + version. +- **Release notes:** generated for the combined `vX.Y.Z` GitHub + Release. Write descriptive PR titles for changes that touch the Rust + surface so they are represented accurately in the shared notes. ## Cargo prerelease semantics @@ -59,8 +57,8 @@ cargo yank --version X.Y.Z github-copilot-sdk Yanking does *not* delete the version — existing `Cargo.lock` files keep working — but it stops new resolutions from picking it. Follow -up with a patch release that fixes the bug, and add a note to the -yanked version's GitHub Release explaining why. +up with a patch release that fixes the bug, and update the combined +GitHub Release notes to explain why. Reverse with `cargo yank --undo --version X.Y.Z github-copilot-sdk` if the yank was a mistake. @@ -90,6 +88,5 @@ git push origin rust/vX.Y.Z perl -i -pe 's/^version = ".*"$/version = "0.0.0-dev"/' Cargo.toml ``` -Manual publishes skip the auto-generated GitHub Release. Run -`gh release create rust/vX.Y.Z --generate-notes` after pushing the -tag. +Manual publishes skip the combined GitHub Release. Create or update the +matching `vX.Y.Z` release after pushing the tag. From f75d222117d99acf50db95fab8bb2cbf1d5148d8 Mon Sep 17 00:00:00 2001 From: Matthew Rayermann Date: Tue, 11 Aug 2026 09:31:27 -0700 Subject: [PATCH 53/72] [SDK/Factories] Make The Agent Factories Surface Match The Wire Contract (#2309) * [SDK/Factories] Make The Agent Factories Surface Match The Wire Contract Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Codegen] Map Both Opaque Schema Markers In The TypeScript Generator A bare x-opaque-json node now renders as JsonValue and a bare x-opaque-in-process node as OpaqueInProcessValue, instead of both collapsing to an object index signature. Nodes that also carry a real constraint keep it, so declarations like ExternalToolResult and McpServerConfig retain their unions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Drop The FactoryRunResult Override And The Factory Casts The regenerated wire types now type the factory result and argument fields as JsonValue, so the hand-written FactoryRunResult override, the toPublicFactoryRunResult boundary helper, and four casts are all unnecessary. A compile-time assertion pins the result type so the override cannot creep back. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Refuse A Factory Run Started From Inside A Factory Body A factory body could start a second top-level run through any session reference it could reach, escaping the limits the user approved. An AsyncLocalStorage guard now refuses factory.run and factory.resume on the body's call path, before the RPC is dispatched, so no durable run row is created. The guard is per-call-path, so an unrelated concurrent run started elsewhere in the extension still succeeds. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Correct The Factory Resume Error Code Union The union exported two codes no runtime path raises and omitted five it does, so a caller could branch on a dead code and receive a raw RpcResponseError for a real one. It now names exactly the codes execute_resume raises before a resumed run starts. permission_denied is deliberately excluded: an SDK-initiated resume dispatches with RunOrigin::default(), so the approval branch never runs and the code is unreachable from this path. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Forward Every Declared Subagent Option From ctx.agent The hand-written FactoryAgentOptions declared only label, schema and model, and the agent implementation rebuilt the request from those three, so agent, reasoningEffort and contextTier were dropped before the request was sent. The options are now declared once as a key tuple and copied from it, and two compile-time assertions pin that tuple to both the public and the wire interface, so a future wire option fails the build instead of being silently dropped. Undeclared keys are still filtered out, because the wire schema forbids them. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Stop A Latched Progress Flush Error From Downgrading A Run A background progress flush that failed earlier latched its error, and close() rethrew it from the factory execute finally block, so a factory body that succeeded settled as an error. The latched error is now best effort and warns, matching the treatment the final send already had. A mid-body flush failure stays fatal, because a running body that cannot record progress must not continue. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * [SDK/Factories] Correct The Factories Guide And Published API Comments The guide and four JSDoc comments described behavior that does not exist: a declined SDK-initiated run resolving as cancelled, a single-active-run limit, two error codes no runtime path raises, an unpaginated listRuns, and a three-option ctx.agent. They now match the shipped surface, including that the SDK forwards agent, reasoningEffort and contextTier while the current runtime does not yet honor them. File-content assertions guard both files, which nothing else covers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Add changelog entry for the Agent Factories wire-contract corrections Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Drop the transient runtime-support caveat from the factories docs The claim that the runtime does not yet honor agent, reasoningEffort and contextTier is a point-in-time fact about another repository. It rots as soon as the runtime lands support, so the SDK docs no longer carry it. Also wraps three over-length test assertions that the prettier check flagged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Simplify factory docs so they do not encode transient facts - Describe ctx.session by what it omits, and point at the extensions_manage guide - Drop the hardcoded active-run limit, which will become a setting - Drop the listRuns paging parenthetical Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Stop the subagent-option E2E from waiting on a model response The factory awaited its subagent to completion, so the test hung wherever no cached model response exists and timed out at 30s on CI. Only the runtime's acceptance of the option payload is under test, and a refused request rejects before a subagent starts. The factory now races the call against a short timer and returns as soon as the request is accepted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Describe ctx.session by how it behaves, not by what it lacks The context session is a full CopilotSession, so factory.run and factory.resume are present and callable. Saying the APIs are absent contradicted the exported type. The guide and the published comment now say the session refuses those calls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd * Drop the hand-written changelog entry The changelog is generated at release time, so an entry added by hand in a feature PR does not fit the file's convention. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd --------- Copilot-Session: 5ece6b29-8b10-47aa-ab17-b64c47f5fdcd --- nodejs/docs/factories.md | 8 +- nodejs/src/factory.ts | 72 ++-- nodejs/src/generated/rpc.ts | 268 +++++--------- nodejs/src/generated/session-events.ts | 149 +++----- nodejs/src/index.ts | 9 +- nodejs/src/session.ts | 97 ++--- nodejs/src/types.ts | 3 +- nodejs/test/e2e/factory.e2e.test.ts | 297 +++++++++++++-- .../test/e2e/fixtures/factory-extension.mjs | 154 +++++++- nodejs/test/factory.test.ts | 347 +++++++++++++++++- nodejs/test/session-event-types.test.ts | 19 + nodejs/test/typescript-codegen.test.ts | 122 ++++++ scripts/codegen/typescript.ts | 83 ++++- scripts/codegen/utils.ts | 28 ++ 14 files changed, 1245 insertions(+), 411 deletions(-) diff --git a/nodejs/docs/factories.md b/nodejs/docs/factories.md index 0c1f0f09a..a22767905 100644 --- a/nodejs/docs/factories.md +++ b/nodejs/docs/factories.md @@ -53,7 +53,7 @@ The `run()` context provides: - `ctx.runId`: Stable ID reused across resumed attempts. - `ctx.args`: Invocation arguments, forwarded verbatim. When the caller omits `args`, this is `{}` rather than `undefined`. -- `ctx.agent(prompt, options?)`: Runs one factory-owned subagent. Options are exactly `label`, `schema`, and `model`. See [Subagent calls](#subagent-calls). +- `ctx.agent(prompt, options?)`: Runs one factory-owned subagent. Options are exactly `label`, `schema`, `model`, `agent`, `reasoningEffort`, and `contextTier`. See [Subagent calls](#subagent-calls). - `ctx.parallel(thunks)`: Runs thunks concurrently and awaits all of them (a barrier). A thunk that throws becomes `null` in the result array, so one failed item does not lose the rest. Cancellation and hard runtime failures (`ResponseError`, `ConnectionError`) are the exception — those propagate and reject the whole call, because they mean the run itself is in trouble rather than one item having failed. Handle them at run level; do not assume every failure arrives as a `null`. Rejects above 4096 items. - `ctx.pipeline(items, ...stages)`: Flows each item through every stage without a barrier between stages, so one item can be in a later stage while another is still in an earlier one. Each stage is called as `(previous, item, index)`, where `previous` is the prior stage's result and `item` is the original input. A stage that throws drops that item to `null` and skips its remaining stages, with the same exception for cancellation and hard runtime failures. Rejects above 4096 items. - `ctx.phase(title)`: Starts a named progress phase. This sets a single run-global value, so calling it from inside concurrent `parallel`/`pipeline` stages races. Call it at run-level transitions and distinguish concurrent work by `label` instead. @@ -61,7 +61,7 @@ The `run()` context provides: - `ctx.step(key, producer, options?)`: Journals the producer's JSON result under a stable key so a resume replays it without re-running the producer. A journaled (default) producer must return a JSON-serializable value; `undefined` or a non-JSON value is rejected. Pass `{ volatile: true }` to bypass the journal and run the producer every time. The key is the *sole* identity: neither the producer body nor its inputs contribute to it. A resume replays the cached value for a matching key even if the producer has since changed, so version the key (`"scan-v2"`) whenever its inputs or meaning change. Journaled producers are best-effort at-least-once and may run again across crashes or concurrent same-key callers, so keep side effects idempotent. -- `ctx.session`: The full session returned by `joinSession`. +- `ctx.session`: The session returned by `joinSession`. It refuses calls that start or resume a factory run. Call `extensions_manage` with `operation: "guide"` to read more about the session APIs. - `ctx.signal`: Cooperative cancellation signal for extension work and subprocesses. - `ctx.factory(...)`: Always rejects because nested factories are not supported. @@ -156,7 +156,7 @@ session.factory.resume( ): Promise; ``` -Both resolve with the run envelope (`FactoryRunResult`) for **every** outcome — `completed`, `error`, `halted`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. A declined fresh run is not a pre-execution failure: the run row already exists by the time the prompt is answered, so it resolves with a terminal `cancelled` envelope carrying the run ID. Only failures that occur *before* a run exists reject: an unknown factory name or an already-active session. Pre-execution resume failures, including a declined reapproval, throw `FactoryResumeError`, whose `code` is one of `not_found`, `non_resumable`, `already_active`, `reapproval_declined`, or `no_approval_provider`. +Both resolve with the run envelope (`FactoryRunResult`) for **every** outcome — `completed`, `error`, `halted`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. SDK-initiated `run` and `resume` do not request permission, so they have no declined outcome. The model's `run_factory` tool requests permission before the durable row exists; declining it creates no run row. An SDK-initiated run is refused only when the session already has its maximum number of active top-level runs. Pre-execution resume failures throw `FactoryResumeError`, whose `code` is one of `not_found`, `non_resumable`, `already_active`, `factory_already_running`, `factory_limits_invalid`, `factory_session_disposed`, `factory_storage_unavailable`, or `factory_storage_corrupt`. An agent that no longer has a prior run's ID in context can recover it with `factories_manage` and `operation: "runs"`, which lists the session's factory runs with their IDs and statuses. This matters for resume: a run that reached a limit keeps its journal, so resuming it replays completed work for free, while restarting it from scratch pays for that work twice. @@ -210,7 +210,7 @@ const page = await session.factory.getRunProgress(runId, { }); ``` -- `listRuns()` returns summaries in durable creation order. +- `listRuns()` returns the newest default page of this session's durable factory runs. - `getRunDetail(runId)` returns phases, prompt-safe agent summaries, and the latest progress page. - `getRunProgress(runId, options?)` pages progress forward, backward, by phase, or from the latest tail. diff --git a/nodejs/src/factory.ts b/nodejs/src/factory.ts index 53c0aeca8..8ad1c7acb 100644 --- a/nodejs/src/factory.ts +++ b/nodejs/src/factory.ts @@ -6,38 +6,15 @@ import type { FactoryGetRunProgressRequest, FactoryProgressPage, FactoryRunDetail, - FactoryRunResult as WireFactoryRunResult, + FactoryRunResult, FactoryRunStatus, FactoryRunSummary, } from "./generated/rpc.js"; +import type { ContextTier } from "./generated/session-events.js"; import type { CopilotSession } from "./session.js"; import type { FactoryLimits, FactoryMeta } from "./types.js"; -/** - * The envelope describing a factory run: its identity, status, and — once it - * has completed — its result. `getRun` returns this for an in-flight run too, - * so `status` may be `pending` or `running` and the outcome fields absent. - * - * `result` is re-typed here rather than taken from the generated wire type. The - * runtime returns any JSON value — including `null`, a string, a number, or an - * array — but the schema models the field as an opaque node, which the - * generator renders as an object. Narrowing the correction to this surface - * keeps the `x-opaque-json` handling unchanged for every other consumer. - * - * This override is temporary. Once the schema distinguishes an opaque JSON - * value from an opaque in-process value and that ships in a CLI release, - * regenerating produces the right type directly, and this declaration, the - * `toPublicFactoryRunResult` boundary helper, and the casts around it should - * all be deleted. Tracked by github/copilot-agent-runtime#14122. - * - * @experimental Part of the experimental Agent Factories surface and may - * change or be removed in future SDK or CLI releases. - */ -export type FactoryRunResult = Omit & { - /** Completed factory result. */ - result?: JsonValue; -}; - +export type { FactoryRunResult }; export type { FactoryAgentSummary, FactoryPhaseStatus, @@ -115,8 +92,20 @@ export interface FactoryAgentOptions { label?: string; schema?: FactoryJsonSchema; model?: string; + reasoningEffort?: string; + contextTier?: ContextTier; + agent?: string; } +export const FACTORY_AGENT_OPTION_KEYS = [ + "label", + "schema", + "model", + "reasoningEffort", + "contextTier", + "agent", +] as const; + /** * Options for a durable factory step. * @@ -185,7 +174,10 @@ export interface FactoryContext { factory(name: string, args?: JsonValue): Promise; /** Caller-supplied input, forwarded verbatim. */ args: TArgs; - /** The same full session instance returned by `joinSession`. */ + /** + * The session instance returned by `joinSession`. It refuses calls that + * start or resume a factory run. + */ session: CopilotSession; /** Cooperative cancellation signal for the current factory run. */ signal: AbortSignal; @@ -275,8 +267,11 @@ export type FactoryResumeErrorCode = | "not_found" | "non_resumable" | "already_active" - | "reapproval_declined" - | "no_approval_provider"; + | "factory_already_running" + | "factory_limits_invalid" + | "factory_session_disposed" + | "factory_storage_unavailable" + | "factory_storage_corrupt"; /** * Friendly factory API exposed on a session. @@ -290,9 +285,12 @@ export interface SessionFactoryApi { * * The envelope is returned for every outcome, including `error`, `halted`, * and `cancelled` — inspect `status` and read `result` only when the run - * completed. A declined fresh run resolves with a terminal `cancelled` - * envelope. Failures that occur before a run exists (such as an unknown - * factory or an already-active session) still reject. + * completed. SDK-initiated runs do not request permission, so they have no + * declined outcome. The model's `run_factory` tool requests permission + * before a durable row exists; declining it creates no run row. Failures + * that occur before a run exists (such as an unknown factory or attempting + * to start a run while the session is at its active top-level run limit) + * still reject. */ run(name: string, options?: RunOptions): Promise; run( @@ -302,9 +300,9 @@ export interface SessionFactoryApi { /** * Resume a run from its persisted factory name, arguments, journal, and accounting. * - * Resolves with the run envelope like {@link SessionFactoryApi.run}. A - * pre-execution failure, including declined reapproval, rejects with - * {@link FactoryResumeError}. + * Resolves with the run envelope like {@link SessionFactoryApi.run}. + * SDK-initiated resumes do not request permission. A pre-execution failure + * with a documented resume code rejects with {@link FactoryResumeError}. */ resume(runId: string, options?: ResumeOptions): Promise; /** Read the latest durable envelope for a factory run. */ @@ -324,7 +322,9 @@ export interface SessionFactoryApi { * {@link SessionFactoryApi.cancel} to actually stop it. */ waitForRun(runId: string, options?: { signal?: AbortSignal }): Promise; - /** List this session's durable factory runs in creation order. */ + /** + * List the newest default page of this session's durable factory runs. + */ listRuns(): Promise; /** Read durable phases, direct agents, and the latest progress tail for a run. */ getRunDetail(runId: string): Promise; diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 042a7d0bb..cefc8ef4d 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -7,6 +7,16 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; import type { AbortReason, Attachment, ContextTier, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionEvent, SessionLimitsConfig, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval, Verbosity } from "./session-events.js"; +/** A value that can be represented losslessly on the SDK JSON wire. */ +export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }; + +/** + * A value that lives only in this process and never crosses the JSON-RPC + * boundary, such as a callback or a host object handle. + * @internal + */ +export type OpaqueInProcessValue = unknown; + /** * Initial authentication info for the session. * @@ -259,6 +269,22 @@ export type AuthInfoType = | "token" /** Authentication from a Copilot API token. */ | "copilot-api-token"; +/** + * JSON Schema for canvas open input + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CanvasJsonSchema". + */ +/** @experimental */ +export type CanvasJsonSchema = JsonValue; +/** + * Provider-supplied action result. + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "CanvasActionInvokeResult". + */ +/** @experimental */ +export type CanvasActionInvokeResult = JsonValue; /** * Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command * @@ -3605,7 +3631,7 @@ export interface AgentInfo { * @experimental */ mcpServers?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Skill names preloaded into this agent's context. Omitted means none. @@ -4009,16 +4035,6 @@ export interface CanvasAction { description?: string; inputSchema?: CanvasJsonSchema; } -/** - * JSON Schema for canvas open input - * - * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "CanvasJsonSchema". - */ -/** @experimental */ -export interface CanvasJsonSchema { - [k: string]: unknown | undefined; -} /** * Canvas action invocation parameters. * @@ -4038,19 +4054,7 @@ export interface CanvasActionInvokeRequest { /** * Action input */ - input?: { - [k: string]: unknown | undefined; - }; -} -/** - * Provider-supplied action result. - * - * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema - * via the `definition` "CanvasActionInvokeResult". - */ -/** @experimental */ -export interface CanvasActionInvokeResult { - [k: string]: unknown | undefined; + input?: JsonValue; } /** * Canvas close parameters. @@ -4195,9 +4199,7 @@ export interface OpenCanvasInstance { /** * Input supplied when the instance was opened */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; } /** * Canvas open parameters. @@ -4222,9 +4224,7 @@ export interface CanvasOpenRequest { /** * Canvas open input */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; } /** * Canvas close parameters sent to the provider. @@ -4297,9 +4297,7 @@ export interface CanvasProviderInvokeActionRequest { /** * Action input */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; host?: CanvasHostContext; session?: CanvasSessionContext; } @@ -4330,9 +4328,7 @@ export interface CanvasProviderOpenRequest { /** * Canvas open input */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; host?: CanvasHostContext; session?: CanvasSessionContext; } @@ -4657,9 +4653,7 @@ export interface ConfigureSessionExtensionsParams { * * @internal */ - controller?: { - [k: string]: unknown | undefined; - }; + controller?: OpaqueInProcessValue; } /** * Metadata for a connected remote session. @@ -4904,7 +4898,7 @@ export interface CurrentToolMetadata { * JSON Schema for tool input */ input_schema?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Whether the tool is loaded on demand via tool search @@ -5334,9 +5328,7 @@ export interface ExtensionContextPushInput { /** * Caller-supplied JSON payload (required, may be null but not undefined) */ - payload: { - [k: string]: unknown | undefined; - }; + payload: JsonValue; } /** * Opaque integrator-owned process launch profile for one extension entrypoint. @@ -5460,7 +5452,7 @@ export interface ExternalToolTextResultForLlm { * Optional tool-specific telemetry */ toolTelemetry?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Base64-encoded binary results returned to the model @@ -5500,7 +5492,7 @@ export interface ExternalToolTextResultForLlmBinaryResultsForLlm { * Optional metadata from the producing tool. */ metadata?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -5737,9 +5729,7 @@ export interface FactoryAgentOptions { /** * Optional JSON Schema for structured agent output. */ - schema?: { - [k: string]: unknown | undefined; - }; + schema?: JsonValue; /** * Optional model identifier for the subagent. */ @@ -5787,9 +5777,7 @@ export interface FactoryAgentResult { /** * Agent result, omitted when the agent produced no result. */ - result?: { - [k: string]: unknown | undefined; - }; + result?: JsonValue; } /** * Prompt-safe durable identity and live status for a direct factory agent. @@ -5877,9 +5865,7 @@ export interface FactoryExecuteRequest { /** * Factory input value. */ - args: { - [k: string]: unknown | undefined; - }; + args: JsonValue; } /** * Result returned by an extension factory closure. @@ -5892,9 +5878,7 @@ export interface FactoryExecuteResult { /** * Factory result value. */ - result?: { - [k: string]: unknown | undefined; - }; + result?: JsonValue; } /** * Parameters for paging factory progress. @@ -5974,9 +5958,7 @@ export interface FactoryJournalGetResult { /** * Cached JSON result. The hit field distinguishes a cached JSON null from a miss. */ - resultJson?: { - [k: string]: unknown | undefined; - }; + resultJson?: JsonValue; } /** * Parameters for storing a factory journal entry. @@ -6001,9 +5983,7 @@ export interface FactoryJournalPutRequest { /** * JSON result to memoize. */ - resultJson: { - [k: string]: unknown | undefined; - }; + resultJson: JsonValue; } /** * Parameters for paging factory runs. @@ -6283,9 +6263,7 @@ export interface FactoryRunResult { /** * Completed factory result. */ - result?: { - [k: string]: unknown | undefined; - }; + result?: JsonValue; /** * Error message for an errored run. */ @@ -6298,9 +6276,7 @@ export interface FactoryRunResult { /** * Partial journal and progress snapshot for a halted, cancelled, or errored run. */ - snapshot?: { - [k: string]: unknown | undefined; - }; + snapshot?: JsonValue; } /** * Full factory run observability detail. @@ -6348,9 +6324,7 @@ export interface FactoryRunRequest { /** * Factory input value. */ - args: { - [k: string]: unknown | undefined; - }; + args: JsonValue; options?: RunOptions; } /** @@ -6925,7 +6899,7 @@ export interface HistoryTruncateResult { export interface HookInvokeRequest { sessionId: string; hookType: HookType; - input: unknown; + input: JsonValue; } /** * Optional output returned by an SDK callback hook. @@ -6936,7 +6910,7 @@ export interface HookInvokeRequest { /** @experimental */ /** @internal */ export interface HookInvokeResponse { - output?: unknown; + output?: JsonValue; } /** * Installed plugin record from global state, with marketplace, version, install time, enabled state, cache path, and source. @@ -7568,9 +7542,7 @@ export interface ManagedSettingsReadResult { /** * Validated, canonical managed-settings JSON. Omitted when no managed settings were discovered or when discovered settings failed validation. */ - settingsJson?: { - [k: string]: unknown | undefined; - }; + settingsJson?: JsonValue; /** * Discovery or validation error text when managed settings could not be read safely. */ @@ -7741,7 +7713,7 @@ export interface McpAppsCallToolRequest { * Tool arguments */ arguments?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. @@ -7886,7 +7858,7 @@ export interface McpAppsListToolsResult { * App-callable tools from the server */ tools: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }[]; } /** @@ -7947,7 +7919,7 @@ export interface McpAppsResourceContent { * Resource-level metadata (CSP, permissions, etc.) */ _meta?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -8220,9 +8192,7 @@ export interface McpConfigureGitHubRequest { * * @internal */ - authInfo: { - [k: string]: unknown | undefined; - }; + authInfo: OpaqueInProcessValue; } /** * Result of configuring GitHub MCP. @@ -8308,9 +8278,7 @@ export interface McpExecuteSamplingParams { /** * The original MCP JSON-RPC request ID (string or number). Used by the runtime to correlate the inference with the originating MCP request for telemetry; this is distinct from `requestId` (which is the schema-level cancellation handle). */ - mcpRequestId: { - [k: string]: unknown | undefined; - }; + mcpRequestId: JsonValue; request: McpExecuteSamplingRequest; } /** @@ -8683,25 +8651,19 @@ export interface McpRegisterExternalClientRequest { * * @internal */ - client: { - [k: string]: unknown | undefined; - }; + client: OpaqueInProcessValue; /** * In-process MCP Transport instance. Marked internal: cannot be serialized across the JSON-RPC boundary. * * @internal */ - transport: { - [k: string]: unknown | undefined; - }; + transport: OpaqueInProcessValue; /** * In-process server config (MCPServerConfig) paired with the in-process client/transport. Marked internal alongside its companions. * * @internal */ - config: { - [k: string]: unknown | undefined; - }; + config: OpaqueInProcessValue; } /** * Opaque MCP reload configuration. @@ -8717,9 +8679,7 @@ export interface McpReloadWithConfigRequest { * * @internal */ - config: { - [k: string]: unknown | undefined; - }; + config: OpaqueInProcessValue; } /** * Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). @@ -8775,13 +8735,13 @@ export interface McpResource { * Resource-level metadata */ _meta?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Server-provided non-standard descriptor fields preserved from the MCP response */ additionalProperties?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -8812,7 +8772,7 @@ export interface McpResourceIcon { * Server-provided non-standard icon fields preserved from the MCP response */ additionalProperties?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -8839,7 +8799,7 @@ export interface McpResourceAnnotations { * Server-provided non-standard annotation fields preserved from the MCP response */ additionalProperties?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -8870,7 +8830,7 @@ export interface McpResourceContent { * Resource-level metadata (CSP, permissions, etc.) */ _meta?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -8978,13 +8938,13 @@ export interface McpResourceTemplate { * Resource-template-level metadata */ _meta?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Server-provided non-standard descriptor fields preserved from the MCP response */ additionalProperties?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -9947,7 +9907,7 @@ export interface NameSetRequest { /** @experimental */ export interface OptionsUpdateAdditionalContentExclusionPolicy { rules: OptionsUpdateAdditionalContentExclusionPolicyRule[]; - last_updated_at: unknown; + last_updated_at: JsonValue; scope: OptionsUpdateAdditionalContentExclusionPolicyScope; } /** @@ -11025,7 +10985,7 @@ export interface PermissionRulesSet { /** @experimental */ export interface PermissionsConfigureAdditionalContentExclusionPolicy { rules: PermissionsConfigureAdditionalContentExclusionPolicyRule[]; - last_updated_at: unknown; + last_updated_at: JsonValue; scope: PermissionsConfigureAdditionalContentExclusionPolicyScope; } /** @@ -11836,7 +11796,7 @@ export interface ProviderAddResult { /** * Synthesized selectable model entries for the newly added BYOK models, each under its provider-qualified selection id (`provider/id`). Empty when only providers were added. */ - models: unknown[]; + models: JsonValue[]; } /** * Custom model-provider configuration (BYOK). @@ -12471,9 +12431,7 @@ export interface QueueConsumeSystemNotificationsRequest { /** * Opaque runtime-owned filter object. */ - filter: { - [k: string]: unknown | undefined; - }; + filter: JsonValue; } /** * Inputs for marking session.idle deferred in native state. @@ -12882,9 +12840,7 @@ export interface RegisterExtensionToolsParams { * * @internal */ - loader: { - [k: string]: unknown | undefined; - }; + loader: OpaqueInProcessValue; options?: SessionsRegisterExtensionToolsOnSessionOptions; } /** @@ -12900,9 +12856,7 @@ export interface SessionsRegisterExtensionToolsOnSessionOptions { * * @internal */ - enabled?: { - [k: string]: unknown | undefined; - }; + enabled?: OpaqueInProcessValue; } /** * Handle for releasing the extension tool registration. @@ -12920,9 +12874,7 @@ export interface RegisterExtensionToolsResult { * * @internal */ - unsubscribe: { - [k: string]: unknown | undefined; - }; + unsubscribe: OpaqueInProcessValue; } /** * Opaque handle previously returned by `registerInterest` to release. @@ -13043,9 +12995,7 @@ export interface RemoteControlStatusActive { * * @internal */ - promptManager?: { - [k: string]: unknown | undefined; - }; + promptManager?: OpaqueInProcessValue; /** * True while a read-only/session-sync export is deferred, awaiting the first `user.message` before its MC session exists. Marked internal: this field is excluded from the public SDK surface and is populated only on the CLI in-process path. * @@ -13867,15 +13817,11 @@ export interface SendSystemNotificationRequest { /** * Optional structured notification kind. */ - kind?: { - [k: string]: unknown | undefined; - }; + kind?: JsonValue; /** * Internal delivery options, including passive policy. */ - options?: { - [k: string]: unknown | undefined; - }; + options?: JsonValue; } /** * Agents discovered across user, project, plugin, and remote sources. @@ -14363,7 +14309,7 @@ export interface SessionFsSqliteQueryRequest { * Optional named bind parameters */ params?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -14378,7 +14324,7 @@ export interface SessionFsSqliteQueryResult { * For SELECT: array of row objects. For others: empty array. */ rows: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }[]; /** * Column names from the result set @@ -14436,7 +14382,7 @@ export interface SessionFsSqliteTransactionStatement { * Optional named bind parameters. */ params?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -14839,7 +14785,7 @@ export interface SessionModelList { /** * Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`). */ - list: unknown[]; + list: JsonValue[]; /** * Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable. */ @@ -14848,7 +14794,7 @@ export interface SessionModelList { * Per-quota snapshots returned alongside the model list, keyed by quota type. */ quotaSnapshots?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -14909,9 +14855,7 @@ export interface SessionOpenOptions { * * @internal */ - expAssignments?: { - [k: string]: unknown | undefined; - }; + expAssignments?: JsonValue; /** * Opt-in: self-fetch and enforce enterprise managed settings at session bootstrap. */ @@ -15166,7 +15110,7 @@ export interface ShellInitScript { /** @experimental */ export interface SessionOpenOptionsAdditionalContentExclusionPolicy { rules: SessionOpenOptionsAdditionalContentExclusionPolicyRule[]; - last_updated_at: unknown; + last_updated_at: JsonValue; scope: SessionOpenOptionsAdditionalContentExclusionPolicyScope; } /** @@ -15315,9 +15259,7 @@ export interface SessionsOpenCloud { * * @internal */ - onTaskCreated?: { - [k: string]: unknown | undefined; - }; + onTaskCreated?: OpaqueInProcessValue; } /** * Parameters for fetching a remote session and handing it off to a new local session. @@ -15339,17 +15281,13 @@ export interface SessionsOpenHandoff { * * @internal */ - onProgress?: { - [k: string]: unknown | undefined; - }; + onProgress?: OpaqueInProcessValue; /** * In-process confirmation callback `(request) => boolean | Promise` invoked when the handoff needs the caller to confirm a non-fatal blocker (e.g. a repository mismatch between the current working directory and the remote session). Returning `true` proceeds with the handoff; returning `false` (or omitting the callback) aborts it. Marked internal because a function reference cannot cross the JSON-RPC boundary, for the same reasons as `onProgress`. * * @internal */ - onConfirm?: { - [k: string]: unknown | undefined; - }; + onConfirm?: OpaqueInProcessValue; } /** * Result of opening a session. @@ -15371,9 +15309,7 @@ export interface SessionOpenResult { * * @internal */ - sessionApi?: { - [k: string]: unknown | undefined; - }; + sessionApi?: OpaqueInProcessValue; /** * Startup prompts queued by user-level hook configs at session creation. Only populated when status is `created`; resumed sessions return an empty array. */ @@ -17316,7 +17252,7 @@ export interface Tool { * JSON Schema for the tool's input parameters */ parameters?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Optional instructions for how to use this tool effectively @@ -17749,17 +17685,13 @@ export interface UIEphemeralQueryRequest { * * @internal */ - onChunk?: { - [k: string]: unknown | undefined; - }; + onChunk?: OpaqueInProcessValue; /** * In-process `AbortSignal` forwarded to the model client to cancel an in-flight request. Marked internal: excluded from the public SDK surface. Replaced by an explicit cancellation token + cancel RPC in the SDK migration. * * @internal */ - abortSignal?: { - [k: string]: unknown | undefined; - }; + abortSignal?: OpaqueInProcessValue; } /** * Transient answer generated from current conversation context. @@ -18210,15 +18142,11 @@ export interface UserSettingMetadata { /** * The effective value: the user's value if set, otherwise the default. */ - value: { - [k: string]: unknown | undefined; - }; + value: JsonValue; /** * The centrally-known default for this setting (null when no default is registered). */ - default: { - [k: string]: unknown | undefined; - }; + default: JsonValue; /** * True when the user has not set an explicit value for this setting (i.e. it is left at its default). Reflects whether the user has overridden the key, not whether the effective value happens to equal the default — a key explicitly set to a value identical to the default still reports false. */ @@ -18250,9 +18178,7 @@ export interface UserSettingsSetRequest { /** * Partial user settings to write, as a free-form object keyed by setting name */ - settings: { - [k: string]: unknown | undefined; - }; + settings: JsonValue; } /** * Outcome of writing user settings. @@ -18481,9 +18407,7 @@ export interface WorkspacesEnsureRequest { /** * Opaque workspace context supplied by the session host. */ - context?: { - [k: string]: unknown | undefined; - }; + context?: JsonValue; } /** * Current workspace metadata for the session, including its absolute filesystem path when available. @@ -18674,9 +18598,7 @@ export interface WorkspacesUpdateMetadataRequest { /** * Opaque workspace context supplied by the session host. */ - context?: { - [k: string]: unknown | undefined; - }; + context?: JsonValue; /** * Optional workspace display name override. */ @@ -18736,7 +18658,7 @@ export interface SessionAgentListRequest { */ /** @experimental */ export interface SessionMcpAppsCallToolResult { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; } /** @experimental */ diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index db24fc23e..4bdfa1994 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -3,6 +3,9 @@ * Generated from: session-events.schema.json */ +/** A value that can be represented losslessly on the SDK JSON wire. */ +export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }; + /** * Union of all session event variants emitted by the Copilot CLI runtime. */ @@ -669,6 +672,10 @@ export type ElicitationCompletedAction = | "decline" /** The user dismissed the request. */ | "cancel"; +/** + * Opaque JSON value submitted for one field in accepted `elicitation.completed` form content. + */ +export type ElicitationCompletedContent = JsonValue | undefined; /** * Reason the runtime is requesting host-provided MCP OAuth credentials */ @@ -709,6 +716,10 @@ export type McpHeadersRefreshCompletedOutcome = | "none" /** No response arrived within the bounded window. */ | "timeout"; +/** + * Source-defined JSON payload for the custom notification + */ +export type CustomNotificationPayload = JsonValue; /** * The user's auto-mode-switch choice */ @@ -3269,9 +3280,7 @@ export interface AttachmentExtensionContext { /** * Caller-supplied JSON payload */ - payload?: { - [k: string]: unknown | undefined; - }; + payload?: JsonValue; /** * Human-readable composer pill label */ @@ -3810,9 +3819,7 @@ export interface CitationReference { /** * Provider-native citation correlation data (e.g. Anthropic search_result_index / document_index), passed through opaquely for debugging and forward compatibility. */ - providerMetadata?: { - [k: string]: unknown | undefined; - }; + providerMetadata?: JsonValue; /** * Identifier of the CitationSource this reference points to (CitationSource.id). */ @@ -3881,9 +3888,9 @@ export interface AssistantMessageServerTools { functionCallNamespaces?: { [k: string]: string | undefined; }; - items?: unknown[]; + items?: JsonValue[]; provider: string; - rawContentBlocks?: unknown[]; + rawContentBlocks?: JsonValue[]; } /** * A tool invocation request from the assistant @@ -3892,9 +3899,7 @@ export interface AssistantMessageToolRequest { /** * Arguments to pass to the tool, format depends on the tool */ - arguments?: { - [k: string]: unknown | undefined; - }; + arguments?: JsonValue; /** * Resolved intention summary describing what this specific call does */ @@ -4573,9 +4578,7 @@ export interface ToolUserRequestedData { /** * Arguments for the tool invocation */ - arguments?: { - [k: string]: unknown | undefined; - }; + arguments?: JsonValue; /** * Unique identifier for this tool call */ @@ -4622,9 +4625,7 @@ export interface ToolExecutionStartData { /** * Arguments passed to the tool */ - arguments?: { - [k: string]: unknown | undefined; - }; + arguments?: JsonValue; /** * When true, the tool output should be displayed expanded (verbatim) in the CLI timeline */ @@ -4848,9 +4849,7 @@ export interface ToolExecutionCompleteData { * * @experimental */ - mcpMeta?: { - [k: string]: unknown | undefined; - }; + mcpMeta?: JsonValue; /** * Model identifier that generated this tool call */ @@ -4879,7 +4878,7 @@ export interface ToolExecutionCompleteData { * Tool-specific telemetry data (e.g., CodeQL check counts, grep match counts) */ toolTelemetry?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event @@ -4932,15 +4931,11 @@ export interface ToolExecutionCompleteResult { * * @experimental */ - mcpMeta?: { - [k: string]: unknown | undefined; - }; + mcpMeta?: JsonValue; /** * Structured content (arbitrary JSON) returned verbatim by the MCP tool */ - structuredContent?: { - [k: string]: unknown | undefined; - }; + structuredContent?: JsonValue; uiResource?: ToolExecutionCompleteUIResource; } /** @@ -4959,7 +4954,7 @@ export interface PersistedBinaryImage { * Optional metadata from the producing tool. */ metadata?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * MIME type of the binary data @@ -4984,7 +4979,7 @@ export interface OmittedBinaryResult { * Optional metadata from the producing tool. */ metadata?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * MIME type of the omitted binary data @@ -5014,7 +5009,7 @@ export interface BinaryAssetReference { * Optional metadata from the producing tool. */ metadata?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * MIME type of the referenced binary data @@ -5779,9 +5774,7 @@ export interface HookStartData { /** * Input data passed to the hook */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; } /** * Session event "hook.end". Hook invocation completion details including output, success status, and error information @@ -5829,9 +5822,7 @@ export interface HookEndData { /** * Output data produced by the hook */ - output?: { - [k: string]: unknown | undefined; - }; + output?: JsonValue; /** * Whether the hook completed successfully */ @@ -5952,7 +5943,7 @@ export interface BinaryAssetData { * Optional metadata from the producing tool. */ metadata?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * MIME type of the binary asset @@ -6021,7 +6012,7 @@ export interface SystemMessageMetadata { * Template variables used when constructing the prompt */ variables?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; } /** @@ -6226,9 +6217,7 @@ export interface SystemNotificationFactoryCompleted { /** * Machine-readable terminal failure details, when present. */ - failure?: { - [k: string]: unknown | undefined; - }; + failure?: JsonValue; /** * Bounded prompt-safe preview of the completed result. */ @@ -6254,9 +6243,7 @@ export interface SystemNotificationUnclassified { /** * Opaque metadata supplied by the external host, when present. */ - metadata?: { - [k: string]: unknown | undefined; - }; + metadata?: JsonValue; /** * Type discriminator. Always "unclassified". */ @@ -6309,9 +6296,7 @@ export interface PermissionRequestedData { /** * Neutral risk metadata supplied by the tool host. Consumers may display this value but must not use it to bypass the permission decision. */ - riskAssessment?: { - [k: string]: unknown | undefined; - }; + riskAssessment?: JsonValue; } /** * Shell command permission request @@ -6494,9 +6479,7 @@ export interface PermissionRequestMcp { /** * Arguments to pass to the MCP tool */ - args?: { - [k: string]: unknown | undefined; - }; + args?: JsonValue; /** * Permission kind discriminator */ @@ -6597,9 +6580,7 @@ export interface PermissionRequestCustomTool { /** * Arguments to pass to the custom tool */ - args?: { - [k: string]: unknown | undefined; - }; + args?: JsonValue; /** * Permission kind discriminator */ @@ -6632,9 +6613,7 @@ export interface PermissionRequestHook { /** * Arguments of the tool call being gated */ - toolArgs?: { - [k: string]: unknown | undefined; - }; + toolArgs?: JsonValue; /** * Tool call ID that triggered this permission request */ @@ -6893,9 +6872,7 @@ export interface PermissionPromptRequestMcp { /** * Arguments to pass to the MCP tool */ - args?: { - [k: string]: unknown | undefined; - }; + args?: JsonValue; /** * Auto-approval judge information for this request; present only when auto mode is enabled. * @@ -7010,9 +6987,7 @@ export interface PermissionPromptRequestCustomTool { /** * Arguments to pass to the custom tool */ - args?: { - [k: string]: unknown | undefined; - }; + args?: JsonValue; /** * Auto-approval judge information for this request; present only when auto mode is enabled. * @@ -7081,9 +7056,7 @@ export interface PermissionPromptRequestHook { /** * Arguments of the tool call being gated */ - toolArgs?: { - [k: string]: unknown | undefined; - }; + toolArgs?: JsonValue; /** * Tool call ID that triggered this permission request */ @@ -7663,7 +7636,7 @@ export interface ElicitationRequestedSchema { * Form field definitions, keyed by field name */ properties: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * List of required field names @@ -7720,12 +7693,6 @@ export interface ElicitationCompletedData { */ requestId: string; } -/** - * Opaque JSON value submitted for one field in accepted `elicitation.completed` form content. - */ -export interface ElicitationCompletedContent { - [k: string]: unknown | undefined; -} /** * Session event "sampling.requested". Sampling request from an MCP server; contains the server name and a requestId for correlation */ @@ -7763,9 +7730,7 @@ export interface SamplingRequestedData { /** * The JSON-RPC request ID from the MCP protocol */ - mcpRequestId: { - [k: string]: unknown | undefined; - }; + mcpRequestId: JsonValue; /** * Unique identifier for this sampling request; used to respond via session.respondToSampling() */ @@ -8114,12 +8079,6 @@ export interface CustomNotificationData { */ version?: number; } -/** - * Source-defined JSON payload for the custom notification - */ -export interface CustomNotificationPayload { - [k: string]: unknown | undefined; -} /** * Optional source-defined string identifiers describing the payload subject */ @@ -8163,9 +8122,7 @@ export interface ExternalToolRequestedData { /** * Arguments to pass to the external tool */ - arguments?: { - [k: string]: unknown | undefined; - }; + arguments?: JsonValue; /** * Unique identifier for this request; used to respond via session.respondToExternalTool() */ @@ -8718,9 +8675,7 @@ export interface ManagedSettingsResolvedData { /** * The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force. */ - settings?: { - [k: string]: unknown | undefined; - }; + settings?: JsonValue; source: ManagedSettingsResolvedSource; } /** @@ -9570,9 +9525,7 @@ export interface CanvasOpenedData { /** * Input supplied when the instance was opened */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; /** * Stable caller-supplied canvas instance identifier */ @@ -9667,9 +9620,7 @@ export interface CanvasRegistryChangedCanvas { /** * JSON Schema for canvas open input */ - inputSchema?: { - [k: string]: unknown | undefined; - }; + inputSchema?: JsonValue; } /** * A single action within a canvas declaration, with its name, optional description, and optional input schema. @@ -9683,9 +9634,7 @@ export interface CanvasRegistryChangedCanvasAction { /** * JSON Schema for action input */ - inputSchema?: { - [k: string]: unknown | undefined; - }; + inputSchema?: JsonValue; /** * Action name */ @@ -9836,9 +9785,7 @@ export interface CanvasRecordedData { /** * Input supplied when the instance was opened */ - input?: { - [k: string]: unknown | undefined; - }; + input?: JsonValue; /** * Stable caller-supplied canvas instance identifier */ @@ -9974,7 +9921,7 @@ export interface McpAppToolCallCompleteData { * Arguments passed to the tool by the app view, if any */ arguments?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Wall-clock duration of the underlying tools/call in milliseconds @@ -9985,7 +9932,7 @@ export interface McpAppToolCallCompleteData { * Standard MCP CallToolResult returned by the server. Present whether or not the call set isError. */ result?: { - [k: string]: unknown | undefined; + [k: string]: JsonValue | undefined; }; /** * Name of the MCP server hosting the tool diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 5ab53471a..622fd38b5 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -41,14 +41,15 @@ export { // consumers can import them directly from "@github/copilot-sdk" instead of // reaching into the package's internal dist layout. See issue #1156. // -// Five names from this file are also explicitly exported elsewhere in this +// Six names from this file are also explicitly exported elsewhere in this // module — `SessionEvent` (re-exported below from `./types.js`), // `PermissionRequest` (re-exported below from `./types.js`), // `PermissionRequestedData`/`PermissionRequestedEvent` (also re-exported below -// from `./types.js`), and `AssistantMessageEvent` (re-exported above from -// `./session.js`). Per the ECMAScript module spec, the explicit named re-exports +// from `./types.js`), `AssistantMessageEvent` (re-exported above from +// `./session.js`), and `JsonValue` (re-exported below from `./factory.js`). +// Per the ECMAScript module spec, the explicit named re-exports // shadow the names arriving via `export type *`, so the hand-authored public API -// surface for those five identifiers is preserved unchanged. +// surface for those six identifiers is preserved unchanged. export type * from "./generated/session-events.js"; export type { CommandContext, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 5cc49fb75..42e30d6f3 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -7,6 +7,7 @@ * @module session */ +import { AsyncLocalStorage } from "node:async_hooks"; import type { MessageConnection } from "vscode-jsonrpc/node.js"; import { ConnectionError, ErrorCodes, ResponseError } from "vscode-jsonrpc/node.js"; import { createSessionRpc } from "./generated/rpc.js"; @@ -16,9 +17,6 @@ import type { CurrentToolMetadata, McpOauthPendingRequestResponse, FactoryLogLine, - FactoryRunRequest, - FactoryExecuteResult, - FactoryJournalPutRequest, FactoryRunResult as WireFactoryRunResult, } from "./generated/rpc.js"; import { type Canvas, CanvasError } from "./canvas.js"; @@ -66,11 +64,13 @@ import type { UserInputResponse, } from "./types.js"; import { + FACTORY_AGENT_OPTION_KEYS, getFactoryDefinition, FactoryResumeError, isFactoryRunTerminal, type FactoryResumeErrorCode, type FactoryRunResult, + type FactoryAgentOptions, type RunOptions, type SessionFactoryApi, type FactoryContext, @@ -84,11 +84,35 @@ function isFactoryResumeErrorCode(value: unknown): value is FactoryResumeErrorCo value === "not_found" || value === "non_resumable" || value === "already_active" || - value === "reapproval_declined" || - value === "no_approval_provider" + value === "factory_already_running" || + value === "factory_limits_invalid" || + value === "factory_session_disposed" || + value === "factory_storage_unavailable" || + value === "factory_storage_corrupt" ); } +function copyDefinedFactoryAgentOption( + source: FactoryAgentOptions, + target: FactoryAgentOptions, + key: TKey +): void { + const value = source[key]; + if (value !== undefined) { + target[key] = value; + } +} + +const factoryExecutionStore = new AsyncLocalStorage<{ active: boolean }>(); + +function throwIfFactoryExecutionIsActive(): void { + if (factoryExecutionStore.getStore()?.active) { + throw new Error( + "factory.run and factory.resume are not allowed while a factory body is running on this call path." + ); + } +} + /** * Convert a raw hook input received over the wire into its public-facing shape. * This deserializes the numeric Unix-ms `timestamp` field on BaseHookInput @@ -255,7 +279,10 @@ class FactoryProgressBuffer { const lines = this.pending.splice(0); await this.flushTail; if (this.flushFailed) { - throw this.flushError; + console.warn( + "Ignoring a background factory progress flush failure after the factory body settled", + this.flushError + ); } if (lines.length > 0) { try { @@ -288,24 +315,6 @@ class FactoryProgressBuffer { } } -/** - * Reconcile the generated envelope with the public one. - * - * The two are identical at runtime. They differ only in how `result` is typed: - * the runtime returns any JSON value, but the schema models the field as an - * opaque node, which the generator renders as an object. {@link FactoryRunResult} - * corrects that for the factory surface without changing `x-opaque-json` - * handling for any other consumer, so the boundary needs a cast rather than a - * conversion. - * - * Delete this along with the {@link FactoryRunResult} override once the schema - * distinguishes opaque JSON values from opaque in-process values — - * github/copilot-agent-runtime#14122. - */ -function toPublicFactoryRunResult(envelope: WireFactoryRunResult): FactoryRunResult { - return envelope as FactoryRunResult; -} - async function awaitFactoryOperation( operation: () => Promise, signal: AbortSignal @@ -442,6 +451,7 @@ export class CopilotSession { nameOrHandle: string | FactoryHandle, options?: RunOptions ): Promise => { + throwIfFactoryExecutionIsActive(); const name = typeof nameOrHandle === "string" ? nameOrHandle @@ -453,9 +463,7 @@ export class CopilotSession { } const envelope = await this.rpc.factory.run({ name, - args: (options?.args === undefined - ? {} - : options.args) as FactoryRunRequest["args"], + args: options?.args === undefined ? {} : options.args, options: { limits: options?.limits, }, @@ -464,6 +472,7 @@ export class CopilotSession { return this.settleFactoryRun(envelope); }) as SessionFactoryApi["run"], resume: (async (runId: string, options?: Parameters[1]) => { + throwIfFactoryExecutionIsActive(); let response; try { response = await this.rpc.factory.resume({ @@ -485,13 +494,13 @@ export class CopilotSession { } return this.settleFactoryRun(response.run); }) as SessionFactoryApi["resume"], - getRun: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.getRun({ runId })), + getRun: async (runId) => this.rpc.factory.getRun({ runId }), waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal), listRuns: async () => (await this.rpc.factory.listRuns({})).runs, getRunDetail: (runId) => this.rpc.factory.getRunDetail({ runId }), getRunProgress: (runId, options = {}) => this.rpc.factory.getRunProgress({ runId, ...options }), - cancel: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.cancel({ runId })), + cancel: async (runId) => this.rpc.factory.cancel({ runId }), }; /** @@ -503,7 +512,7 @@ export class CopilotSession { */ private settleFactoryRun(envelope: WireFactoryRunResult): Promise { if (isFactoryRunTerminal(envelope.status)) { - return Promise.resolve(toPublicFactoryRunResult(envelope)); + return Promise.resolve(envelope); } return this.waitForFactoryRun(envelope.runId); } @@ -562,7 +571,7 @@ export class CopilotSession { rereadRequested = false; const envelope = await this.rpc.factory.getRun({ runId }); if (isFactoryRunTerminal(envelope.status)) { - finish(() => resolve(toPublicFactoryRunResult(envelope))); + finish(() => resolve(envelope)); return; } } while (rereadRequested && !settled); @@ -1379,7 +1388,7 @@ export class CopilotSession { try { const context: FactoryContext = { runId: params.runId, - args: params.args as JsonValue, + args: params.args, session: self, signal: controller.signal, phase: (title: string) => { @@ -1392,17 +1401,17 @@ export class CopilotSession { }, agent: async (prompt, options = {}) => { await progress.flush(); + const opts: FactoryAgentOptions = {}; + for (const key of FACTORY_AGENT_OPTION_KEYS) { + copyDefinedFactoryAgentOption(options, opts, key); + } const response = await awaitFactoryOperation( () => self.rpc.factory.agent({ factoryRunId: params.runId, executionToken: params.executionToken, prompt, - opts: { - label: options.label, - schema: options.schema, - model: options.model, - }, + opts, }), controller.signal ); @@ -1452,8 +1461,7 @@ export class CopilotSession { runId: params.runId, executionToken: params.executionToken, key, - resultJson: - result as FactoryJournalPutRequest["resultJson"], + resultJson: result, }), controller.signal ); @@ -1465,12 +1473,19 @@ export class CopilotSession { throw new Error("nested factories are not supported"); }, }; - const result = await definition.run(context); + const execution = { active: true }; + const result = await factoryExecutionStore.run(execution, async () => { + try { + return await definition.run(context); + } finally { + execution.active = false; + } + }); if (result === undefined) { return {}; } assertFactoryResult(result); - return { result } as FactoryExecuteResult; + return { result }; } finally { try { await progress.close(); diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 4ff279189..3a5f7714b 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -19,6 +19,7 @@ import type { SessionEvent as GeneratedSessionEvent, } from "./generated/session-events.js"; import type { CopilotSession } from "./session.js"; +import type { JsonValue } from "./factory.js"; import type { GitHubTelemetryNotification, ModelBillingTokenPrices, @@ -451,7 +452,7 @@ export type ToolBinaryResult = { description?: string; }; -export type ToolTelemetry = Record | undefined>; +export type ToolTelemetry = Record | undefined>; export type ToolResultObject = { textResultForLlm: string; diff --git a/nodejs/test/e2e/factory.e2e.test.ts b/nodejs/test/e2e/factory.e2e.test.ts index 547ecbbd5..8c038d9de 100644 --- a/nodejs/test/e2e/factory.e2e.test.ts +++ b/nodejs/test/e2e/factory.e2e.test.ts @@ -1,10 +1,10 @@ import { execFileSync } from "node:child_process"; -import { existsSync } from "node:fs"; -import { copyFile, mkdir } from "node:fs/promises"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { copyFile, mkdir, rm } from "node:fs/promises"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { expect, it } from "vitest"; -import { approveAll } from "../../src/index.js"; +import { expect, it, vi } from "vitest"; +import { approveAll, FactoryResumeError } from "../../src/index.js"; import { createSdkTestContext, DEFAULT_GITHUB_TOKEN, @@ -23,55 +23,290 @@ const factoryTestContext = isInProcessTransport }, }); +async function setupFactoryExtension(workDir: string, onPermissionRequest = approveAll) { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + + const { copilotClient, openAiEndpoint } = factoryTestContext; + const extensionDir = join(workDir, ".github", "extensions", "factory-smoke"); + const readyFile = join(extensionDir, "ready"); + await rm(join(workDir, ".github"), { recursive: true, force: true }); + await mkdir(extensionDir, { recursive: true }); + await copyFile( + join(__dirname, "fixtures", "factory-extension.mjs"), + join(extensionDir, "extension.mjs") + ); + execFileSync("git", ["init", "--quiet"], { cwd: workDir }); + + await openAiEndpoint.setCopilotUserByToken(DEFAULT_GITHUB_TOKEN, { + login: "factory-e2e-user", + copilot_plan: "individual_pro", + token_based_billing: true, + is_mcp_enabled: true, + endpoints: { + api: openAiEndpoint.url, + telemetry: "https://localhost:1/telemetry", + }, + analytics_tracking_id: "e2e-test-tracking-id", + }); + + const session = await copilotClient.createSession({ + requestExtensions: true, + extensionSdkPath: resolve(__dirname, "..", "..", "dist"), + onPermissionRequest, + onElicitationRequest: async () => ({ + action: "accept", + content: { action: "approve" }, + }), + }); + + await retry( + "wait for the factory extension to join the session", + async () => { + expect(existsSync(readyFile)).toBe(true); + }, + 300, + 100 + ); + + return session; +} + it.skipIf(isInProcessTransport)( "runs an extension-authored factory across the SDK process boundary", async () => { if (!factoryTestContext) { throw new Error("Factory E2E requires the stdio transport"); } - const { copilotClient, openAiEndpoint, workDir } = factoryTestContext; + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const result = await session.factory.run("argument-echo", { + args: { source: "sdk-e2e", count: 11 }, + }); + + expect(result).toMatchObject({ + status: "completed", + result: { source: "sdk-e2e", count: 11 }, + }); + } +); + +it.skipIf(isInProcessTransport)( + "forwards every declared subagent option to the runtime", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const result = await session.factory.run("forwards-subagent-options"); + + expect(result).toMatchObject({ + status: "completed", + result: { didThrow: false }, + }); + }, + // The factory abandons its subagent once the runtime has accepted the + // request, so the run settles only after the runtime drains that work. + 60_000 +); + +it.skipIf(isInProcessTransport)( + "throws FactoryResumeError with not_found for an unknown run", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const error = await session.factory + .resume("00000000-0000-0000-0000-000000000000") + .catch((caught: unknown) => caught); + + expect(error).toBeInstanceOf(FactoryResumeError); + expect((error as FactoryResumeError).code).toBe("not_found"); + } +); + +it.skipIf(isInProcessTransport)( + "throws FactoryResumeError with non_resumable for a completed run", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const run = await session.factory.run("argument-echo"); + const error = await session.factory.resume(run.runId).catch((caught: unknown) => caught); + + expect(error).toBeInstanceOf(FactoryResumeError); + expect((error as FactoryResumeError).code).toBe("non_resumable"); + } +); + +it.skipIf(isInProcessTransport)( + "runs a factory when its session denies every permission request", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + const denyPermissions = vi.fn(() => ({ kind: "reject" as const })); + await using session = await setupFactoryExtension(workDir, denyPermissions); + + await expect(session.factory.run("argument-echo")).resolves.toMatchObject({ + status: "completed", + }); + expect(denyPermissions).not.toHaveBeenCalled(); + } +); + +it.skipIf(isInProcessTransport)( + "resumes a failed factory when its session denies every permission request", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + const denyPermissions = vi.fn(() => ({ kind: "reject" as const })); + await using session = await setupFactoryExtension(workDir, denyPermissions); - await openAiEndpoint.setCopilotUserByToken(DEFAULT_GITHUB_TOKEN, { - login: "factory-e2e-user", - copilot_plan: "individual_pro", - token_based_billing: true, + const failedRun = await session.factory.run("fails-once"); + expect(failedRun).toMatchObject({ + status: "error", }); + await expect(session.factory.resume(failedRun.runId)).resolves.toMatchObject({ + status: "completed", + result: "resumed", + }); + expect(denyPermissions).not.toHaveBeenCalled(); + } +); + +it.skipIf(isInProcessTransport)( + "refuses a factory started through the context session from a factory body", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const result = await session.factory.run("starts-from-context-session"); + + expect(result).toMatchObject({ + status: "completed", + result: expect.stringContaining("factory.run and factory.resume"), + }); + expect((result as { result: string }).result).toContain("factory body"); + } +); + +it.skipIf(isInProcessTransport)( + "refuses a factory started through the module session from a factory body", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const result = await session.factory.run("starts-from-module-session"); + + expect(result).toMatchObject({ + status: "completed", + result: expect.stringContaining("factory.run and factory.resume"), + }); + expect((result as { result: string }).result).toContain("factory body"); + } +); + +it.skipIf(isInProcessTransport)( + "allows a module-level extension watcher to start a factory while another body is parked", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; const extensionDir = join(workDir, ".github", "extensions", "factory-smoke"); - const readyFile = join(extensionDir, "ready"); - await mkdir(extensionDir, { recursive: true }); - await copyFile( - join(__dirname, "fixtures", "factory-extension.mjs"), - join(extensionDir, "extension.mjs") + await using session = await setupFactoryExtension(workDir); + + const parked = session.factory.run("parked"); + await retry( + "wait for the parked factory to enter its body", + async () => { + expect(existsSync(join(extensionDir, "entered"))).toBe(true); + }, + 100, + 100 ); - execFileSync("git", ["init", "--quiet"], { cwd: workDir }); - - await using session = await copilotClient.createSession({ - requestExtensions: true, - extensionSdkPath: resolve(__dirname, "..", "..", "dist"), - onPermissionRequest: approveAll, - onElicitationRequest: async () => ({ - action: "accept", - content: { action: "approve" }, - }), - }); + writeFileSync(join(extensionDir, "start-b"), "start"); + const bResultFile = join(extensionDir, "b-result"); await retry( - "wait for the factory extension to join the session", + "wait for the module-level watcher factory run to succeed", async () => { - expect(existsSync(readyFile)).toBe(true); + expect(existsSync(bResultFile)).toBe(true); + expect(JSON.parse(readFileSync(bResultFile, "utf8"))).toMatchObject({ + status: "success", + result: { + status: "completed", + result: { source: "module-watcher" }, + }, + }); }, - 300, + 100, 100 ); - const result = await session.factory.run("argument-echo", { - args: { source: "sdk-e2e", count: 11 }, + writeFileSync(join(extensionDir, "release"), "release"); + await expect(parked).resolves.toMatchObject({ + status: "completed", + result: "released", }); + }, + 60_000 +); + +it.skipIf(isInProcessTransport)( + "returns an array result from an extension-authored factory", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const result = await session.factory.run("array-result"); expect(result).toMatchObject({ status: "completed", - result: { source: "sdk-e2e", count: 11 }, + result: [1, "two", false], + }); + } +); + +it.skipIf(isInProcessTransport)( + "passes array factory arguments across the SDK process boundary", + async () => { + if (!factoryTestContext) { + throw new Error("Factory E2E requires the stdio transport"); + } + const { workDir } = factoryTestContext; + await using session = await setupFactoryExtension(workDir); + + const args = [1, "two", false]; + const result = await session.factory.run("argument-echo", { args }); + + expect(result).toMatchObject({ + status: "completed", + result: args, }); } ); diff --git a/nodejs/test/e2e/fixtures/factory-extension.mjs b/nodejs/test/e2e/fixtures/factory-extension.mjs index fab95a90f..5f6c19e2b 100644 --- a/nodejs/test/e2e/fixtures/factory-extension.mjs +++ b/nodejs/test/e2e/fixtures/factory-extension.mjs @@ -1,6 +1,18 @@ -import { writeFileSync } from "node:fs"; +import { existsSync, writeFileSync } from "node:fs"; import { defineFactory, joinSession } from "@github/copilot-sdk/extension"; +const marker = (name) => new URL(`./${name}`, import.meta.url); + +async function waitForMarker(name, timeoutMs) { + const deadline = Date.now() + timeoutMs; + while (!existsSync(marker(name))) { + if (Date.now() >= deadline) { + throw new Error(`Timed out waiting for ${name}`); + } + await new Promise((resolve) => setTimeout(resolve, 50)); + } +} + const argumentEcho = defineFactory({ meta: { name: "argument-echo", @@ -10,5 +22,141 @@ const argumentEcho = defineFactory({ run: async ({ args }) => args, }); -await joinSession({ factories: [argumentEcho] }); -writeFileSync(new URL("./ready", import.meta.url), "ready"); +const arrayResult = defineFactory({ + meta: { + name: "array-result", + description: "Return an array result.", + phases: [], + }, + run: async () => [1, "two", false], +}); + +const forwardsSubagentOptions = defineFactory({ + meta: { + name: "forwards-subagent-options", + description: "Send every declared subagent option to the runtime.", + phases: [], + }, + run: async ({ agent }) => { + // Only the runtime's acceptance of the payload is under test. A refused + // request rejects quickly, because the runtime parses the options before + // it starts a subagent. A subagent that is merely slow to reach a model + // proves the payload was accepted, so waiting for it adds nothing and + // hangs wherever no model is reachable. + const call = agent("Confirm that this request is accepted.", { + agent: "reviewer", + reasoningEffort: "high", + contextTier: "long_context", + }); + // A rejection that lands after the race still needs a handler. + call.catch(() => {}); + let settleTimer; + const stillPending = new Promise((resolve) => { + settleTimer = setTimeout(() => resolve(undefined), 3000); + settleTimer.unref?.(); + }); + try { + await Promise.race([call, stillPending]); + return { didThrow: false }; + } catch { + return { didThrow: true }; + } finally { + clearTimeout(settleTimer); + } + }, +}); + +const startsFromContextSession = defineFactory({ + meta: { + name: "starts-from-context-session", + description: "Try to start a factory through the context session.", + phases: [], + }, + run: async ({ session }) => { + try { + await session.factory.run("argument-echo"); + return "unexpectedly started a factory"; + } catch (error) { + return error instanceof Error ? error.message : String(error); + } + }, +}); + +let session; + +const startsFromModuleSession = defineFactory({ + meta: { + name: "starts-from-module-session", + description: "Try to start a factory through the module session.", + phases: [], + }, + run: async () => { + try { + await session.factory.run("argument-echo"); + return "unexpectedly started a factory"; + } catch (error) { + return error instanceof Error ? error.message : String(error); + } + }, +}); + +const parked = defineFactory({ + meta: { + name: "parked", + description: "Wait for a test-controlled release marker.", + phases: [], + }, + run: async () => { + writeFileSync(marker("entered"), "entered"); + await waitForMarker("release", 30_000); + return "released"; + }, +}); + +const failsOnce = defineFactory({ + meta: { + name: "fails-once", + description: "Fails its first attempt and succeeds when resumed.", + phases: [], + }, + run: async () => { + if (!existsSync(marker("fails-once-attempted"))) { + writeFileSync(marker("fails-once-attempted"), "attempted"); + throw new Error("first attempt failed"); + } + return "resumed"; + }, +}); + +session = await joinSession({ + factories: [ + argumentEcho, + arrayResult, + forwardsSubagentOptions, + startsFromContextSession, + startsFromModuleSession, + parked, + failsOnce, + ], +}); + +void waitForMarker("start-b", 30_000) + .then(async () => { + const result = await session.factory.run("argument-echo", { + args: { source: "module-watcher" }, + }); + writeFileSync(marker("b-result"), JSON.stringify({ status: "success", result })); + }) + .catch((error) => { + if (existsSync(marker("start-b"))) { + writeFileSync( + marker("b-result"), + JSON.stringify({ + status: "error", + error: error instanceof Error ? error.message : String(error), + }) + ); + } + }); + +writeFileSync(marker("ready"), "ready"); diff --git a/nodejs/test/factory.test.ts b/nodejs/test/factory.test.ts index 0282cc4a5..f550c3bc9 100644 --- a/nodejs/test/factory.test.ts +++ b/nodejs/test/factory.test.ts @@ -402,6 +402,65 @@ describe("factories", () => { expect(generatedRpc).toContain("timeoutSeconds?: number;"); }); + it("documents factory invocation and list paging behavior accurately", () => { + const guide = readFileSync(new URL("../docs/factories.md", import.meta.url), "utf8"); + const publicApi = readFileSync(new URL("../src/factory.ts", import.meta.url), "utf8"); + const listRunsPagingWording = "newest default page of this session's durable factory runs"; + const resumeCodes = [ + "not_found", + "non_resumable", + "already_active", + "factory_already_running", + "factory_limits_invalid", + "factory_session_disposed", + "factory_storage_unavailable", + "factory_storage_corrupt", + ]; + const normalizeJSDoc = (document: string) => + document.replace(/\r?\n\s*\* ?/g, " ").replace(/\s+/g, " "); + const normalizedGuide = normalizeJSDoc(guide); + const normalizedPublicApi = normalizeJSDoc(publicApi); + + for (const document of [guide, publicApi]) { + expect(document).not.toContain("reapproval_declined"); + expect(document).not.toContain("no_approval_provider"); + expect(document).not.toMatch(/declined fresh run[\s\S]*terminal `cancelled` envelope/i); + } + + for (const document of [normalizedGuide, normalizedPublicApi]) { + expect(document).toContain(listRunsPagingWording); + } + + expect(normalizedGuide).toContain( + "SDK-initiated `run` and `resume` do not request permission" + ); + expect(normalizedGuide).toContain( + "`run_factory` tool requests permission before the durable row exists" + ); + expect(normalizedGuide).toContain("declining it creates no run row"); + expect(normalizedGuide).toContain("its maximum number of active top-level runs"); + for (const code of resumeCodes) { + expect(guide).toContain(`\`${code}\``); + } + expect(guide).toContain( + "Options are exactly `label`, `schema`, `model`, `agent`, `reasoningEffort`, and `contextTier`" + ); + expect(normalizedGuide).toContain( + "session returned by `joinSession`. It refuses calls that start or resume a factory run" + ); + + expect(normalizedPublicApi).toContain("SDK-initiated runs do not request permission"); + expect(normalizedPublicApi).toContain("declining it creates no run row"); + expect(normalizedPublicApi).toContain( + "while the session is at its active top-level run limit" + ); + expect(normalizedPublicApi).toContain("SDK-initiated resumes do not request permission"); + expect(normalizedPublicApi).toContain("with a documented resume code rejects with"); + expect(normalizedPublicApi).toContain( + "session instance returned by `joinSession`. It refuses calls that start or resume a factory run" + ); + }); + it("serializes only factory metadata in the extension resume payload", async () => { const client = new CopilotClient(); await client.start(); @@ -569,6 +628,133 @@ describe("factories", () => { expect(sendRequest).not.toHaveBeenCalled(); }); + it("keeps factory reads and cancellation available inside a factory body", async () => { + const sendRequest = vi.fn(async (method: string) => { + switch (method) { + case "session.factory.getRun": + return { runId: "other-run", status: "completed" }; + case "session.factory.listRuns": + return { runs: [] }; + case "session.factory.cancel": + return {}; + default: + throw new Error(`Unexpected method: ${method}`); + } + }); + const session = new CopilotSession("session-factory-reads", { sendRequest } as never); + const factory = defineFactory({ + meta: { + name: "factory-reads", + description: "Read factory state from a factory body", + phases: [], + }, + run: async ({ session: contextSession }) => { + const [run, runs] = await Promise.all([ + contextSession.factory.getRun("other-run"), + contextSession.factory.listRuns(), + contextSession.factory.cancel("other-run"), + ]); + return { runId: run.runId, runCount: runs.length }; + }, + }); + session.registerFactories([factory]); + + await expect( + session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "factory-reads", + runId: "run-factory-reads", + executionToken: "execution-token", + args: {}, + }) + ).resolves.toEqual({ result: { runId: "other-run", runCount: 0 } }); + expect(sendRequest).toHaveBeenCalledWith("session.factory.getRun", { + sessionId: session.sessionId, + runId: "other-run", + }); + expect(sendRequest).toHaveBeenCalledWith("session.factory.listRuns", { + sessionId: session.sessionId, + }); + expect(sendRequest).toHaveBeenCalledWith("session.factory.cancel", { + sessionId: session.sessionId, + runId: "other-run", + }); + }); + + it("allows factory.run after a factory body returns", async () => { + const sendRequest = vi.fn(async (method: string) => { + if (method === "session.factory.run") { + return { runId: "run-after-body", status: "completed", result: "started" }; + } + throw new Error(`Unexpected method: ${method}`); + }); + const session = new CopilotSession("session-after-body", { sendRequest } as never); + const factory = defineFactory({ + meta: { + name: "returns", + description: "Return before a separate factory run", + phases: [], + }, + run: async () => "finished", + }); + session.registerFactories([factory]); + + await expect( + session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "returns", + runId: "run-returns", + executionToken: "execution-token", + args: {}, + }) + ).resolves.toEqual({ result: "finished" }); + await expect(session.factory.run("after-body")).resolves.toMatchObject({ + status: "completed", + result: "started", + }); + }); + + it("allows a factory-body timer to start a factory after the body settles", async () => { + const delayedRun = Promise.withResolvers(); + const sendRequest = vi.fn(async (method: string) => { + if (method === "session.factory.run") { + return { runId: "run-from-timer", status: "completed", result: "started" }; + } + throw new Error(`Unexpected method: ${method}`); + }); + const session = new CopilotSession("session-timer", { sendRequest } as never); + const factory = defineFactory({ + meta: { + name: "timer", + description: "Start a factory from an unawaited timer", + phases: [], + }, + run: async () => { + setTimeout(() => { + void session.factory + .run("from-timer") + .then(delayedRun.resolve, delayedRun.reject); + }, 0); + return "finished"; + }, + }); + session.registerFactories([factory]); + + await expect( + session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "timer", + runId: "run-timer", + executionToken: "execution-token", + args: {}, + }) + ).resolves.toEqual({ result: "finished" }); + await expect(delayedRun.promise).resolves.toMatchObject({ + status: "completed", + result: "started", + }); + }); + it("flushes progress incrementally while a factory body is awaiting", async () => { const sendRequest = vi.fn(async () => ({})); const session = new CopilotSession("session-live-progress", { sendRequest } as never); @@ -653,6 +839,95 @@ describe("factories", () => { }); }); + it("forwards every declared factory.agent option", async () => { + const sendRequest = vi.fn(async (method: string) => { + if (method === "session.factory.agent") { + return { result: "pong" }; + } + throw new Error(`Unexpected method: ${method}`); + }); + const session = new CopilotSession("session-agent-options", { sendRequest } as never); + const factory = defineFactory({ + meta: { + name: "agent-options", + description: "Agent option forwarding test", + phases: [], + }, + run: async ({ agent }) => + agent("Reply with pong", { + label: "Pong helper", + model: "gpt-test", + schema: { type: "string" }, + agent: "reviewer", + reasoningEffort: "high", + contextTier: "long_context", + }), + }); + session.registerFactories([factory]); + + await expect( + session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "agent-options", + runId: "run-agent-options", + executionToken: "execution-token", + args: {}, + }) + ).resolves.toEqual({ result: "pong" }); + expect(sendRequest).toHaveBeenCalledWith("session.factory.agent", { + sessionId: session.sessionId, + factoryRunId: "run-agent-options", + executionToken: "execution-token", + prompt: "Reply with pong", + opts: { + label: "Pong helper", + model: "gpt-test", + schema: { type: "string" }, + agent: "reviewer", + reasoningEffort: "high", + contextTier: "long_context", + }, + }); + }); + + it("sends empty factory.agent options when none are supplied", async () => { + const sendRequest = vi.fn(async (method: string) => { + if (method === "session.factory.agent") { + return { result: "pong" }; + } + throw new Error(`Unexpected method: ${method}`); + }); + const session = new CopilotSession("session-empty-agent-options", { + sendRequest, + } as never); + const factory = defineFactory({ + meta: { + name: "empty-agent-options", + description: "Empty agent option forwarding test", + phases: [], + }, + run: async ({ agent }) => agent("Reply with pong"), + }); + session.registerFactories([factory]); + + await expect( + session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "empty-agent-options", + runId: "run-empty-agent-options", + executionToken: "execution-token", + args: {}, + }) + ).resolves.toEqual({ result: "pong" }); + expect(sendRequest).toHaveBeenCalledWith("session.factory.agent", { + sessionId: session.sessionId, + factoryRunId: "run-empty-agent-options", + executionToken: "execution-token", + prompt: "Reply with pong", + opts: {}, + }); + }); + it("keeps each execution token on callbacks from overlapping contexts with the same run id", async () => { const sendRequest = vi.fn(async (method: string) => { if (method === "session.factory.agent") { @@ -1431,6 +1706,56 @@ describe("factories", () => { ); }); + it("keeps a completed execution successful when a background progress flush fails", async () => { + vi.useFakeTimers(); + const release = Promise.withResolvers(); + const sendRequest = vi.fn(async (method: string) => { + if (method === "session.factory.log") { + throw new Error("background transport failure"); + } + return {}; + }); + const warning = vi.spyOn(console, "warn").mockImplementation(() => {}); + const session = new CopilotSession("session-background-flush-failure", { + sendRequest, + } as never); + const factory = defineFactory({ + meta: { + name: "background-flush-failure", + description: "Background flush failure regression test", + phases: [], + }, + run: async ({ log }) => { + log("background line"); + await release.promise; + return "done"; + }, + }); + session.registerFactories([factory]); + + try { + const execution = session.clientSessionApis.factory!.execute({ + sessionId: session.sessionId, + name: "background-flush-failure", + runId: "run-background-flush-failure", + executionToken: "execution-token", + args: {}, + }); + await vi.advanceTimersByTimeAsync(10_000); + await Promise.resolve(); + + release.resolve(); + + await expect(execution).resolves.toEqual({ result: "done" }); + expect(warning).toHaveBeenCalledWith( + "Ignoring a background factory progress flush failure after the factory body settled", + expect.objectContaining({ message: "background transport failure" }) + ); + } finally { + vi.useRealTimers(); + } + }); + it("keeps a mid-run progress flush failure fatal", async () => { const sendRequest = vi.fn(async (method: string) => { if (method === "session.factory.log") { @@ -1736,8 +2061,11 @@ describe("factories", () => { "not_found", "non_resumable", "already_active", - "reapproval_declined", - "no_approval_provider", + "factory_already_running", + "factory_limits_invalid", + "factory_session_disposed", + "factory_storage_unavailable", + "factory_storage_corrupt", ] as const)( "throws FactoryResumeError with code %s for pre-execution failures", async (code) => { @@ -1755,6 +2083,21 @@ describe("factories", () => { } ); + it("leaves an unreachable permission_denied response as a raw ResponseError", async () => { + const session = new CopilotSession("session-resume-permission-denied", { + sendRequest: vi.fn(async () => { + throw new ResponseError(-32602, "resume failed: permission_denied", { + code: "permission_denied", + }); + }), + } as never); + + const error = await session.factory.resume("run-error").catch((caught: unknown) => caught); + expect(error).toBeInstanceOf(ResponseError); + expect(error).not.toBeInstanceOf(FactoryResumeError); + expect((error as ResponseError<{ code: string }>).data.code).toBe("permission_denied"); + }); + it("returns resumed execution failures as envelopes", async () => { const envelope = { runId: "run-execution-error", diff --git a/nodejs/test/session-event-types.test.ts b/nodejs/test/session-event-types.test.ts index 5a8f2ca52..213670216 100644 --- a/nodejs/test/session-event-types.test.ts +++ b/nodejs/test/session-event-types.test.ts @@ -16,6 +16,8 @@ import { describe, expect, it } from "vitest"; import { approveAll } from "../src/index.js"; +import { FACTORY_AGENT_OPTION_KEYS } from "../src/factory.js"; +import type { FactoryAgentOptions as WireFactoryAgentOptions } from "../src/generated/rpc.js"; import type { // The aggregate union; must still resolve via the package root. SessionEvent, @@ -59,6 +61,8 @@ import type { WorkingDirectoryContextHostType, FactoryContext, FactoryDefinition, + FactoryAgentOptions, + FactoryRunResult, JsonValue, } from "../src/index.js"; @@ -97,6 +101,21 @@ type _DefaultFactoryResultIsJsonValueOrVoid = _AssertEqual< JsonValue | void >; const _defaultFactoryResultCheck: _DefaultFactoryResultIsJsonValueOrVoid = true; +type _FactoryRunResultIsJsonValueOrUndefined = _AssertEqual< + FactoryRunResult["result"], + JsonValue | undefined +>; +const _factoryRunResultCheck: _FactoryRunResultIsJsonValueOrUndefined = true; +type _FactoryAgentOptionKeysMatchPublicInterface = _AssertEqual< + (typeof FACTORY_AGENT_OPTION_KEYS)[number], + keyof FactoryAgentOptions +>; +const _factoryAgentOptionKeysCheck: _FactoryAgentOptionKeysMatchPublicInterface = true; +type _PublicFactoryAgentOptionsMatchWire = _AssertEqual< + keyof FactoryAgentOptions, + keyof WireFactoryAgentOptions +>; +const _publicFactoryAgentOptionsCheck: _PublicFactoryAgentOptionsMatchWire = true; // @ts-expect-error Factory arguments must be representable on the JSON wire. type _FactoryArgsRejectUndefined = FactoryContext; // @ts-expect-error Factory results must be JSON values or top-level void. diff --git a/nodejs/test/typescript-codegen.test.ts b/nodejs/test/typescript-codegen.test.ts index e3f3d2857..0a63a5293 100644 --- a/nodejs/test/typescript-codegen.test.ts +++ b/nodejs/test/typescript-codegen.test.ts @@ -48,6 +48,128 @@ describe("typescript schema codegen", () => { ); expect(code).toContain('inlineMode: /** Use a direct value. */ "direct" | "indirect";'); }); + + it("maps bare opaque properties to their marker aliases", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "OpaqueProperty", + type: "object", + properties: { + json: { "x-opaque-json": true }, + inProcess: { "x-opaque-in-process": true }, + }, + required: ["json", "inProcess"], + }), + "OpaqueProperty", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("json: JsonValue;"); + expect(code).toContain("inProcess: OpaqueInProcessValue;"); + }); + + it("maps a bare opaque JSON additional property to JsonValue", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "OpaqueMap", + type: "object", + additionalProperties: { "x-opaque-json": true }, + }), + "OpaqueMap", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("[k: string]: JsonValue;"); + }); + + it("maps a bare opaque JSON array item to JsonValue", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "OpaqueArray", + type: "object", + properties: { values: { type: "array", items: { "x-opaque-json": true } } }, + required: ["values"], + }), + "OpaqueArray", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("values: JsonValue[];"); + }); + + it("maps a bare opaque JSON definition to a named alias", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "OpaqueDefinitionRoot", + type: "object", + properties: { value: { $ref: "#/definitions/OpaqueDefinition" } }, + definitions: { OpaqueDefinition: { "x-opaque-json": true } }, + }), + "OpaqueDefinitionRoot", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("export type OpaqueDefinition = JsonValue;"); + }); + + it("keeps an opaque JSON node with anyOf as a union", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "ConstrainedUnion", + "x-opaque-json": true, + anyOf: [{ type: "string" }, { type: "number" }], + }), + "ConstrainedUnion", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("export type ConstrainedUnion = string | number;"); + expect(code).not.toContain("JsonValue"); + }); + + it("keeps an opaque JSON node with object constraints as an object", async () => { + const code = await compile( + normalizeSchemaForTypeScript({ + title: "ConstrainedObject", + type: "object", + "x-opaque-json": true, + properties: { name: { type: "string" } }, + required: ["name"], + }), + "ConstrainedObject", + { bannerComment: "", style: { semi: true, singleQuote: false } } + ); + + expect(code).toContain("export interface ConstrainedObject {"); + expect(code).toContain("name: string;"); + expect(code).not.toContain("JsonValue"); + }); + + it("removes both opaque markers from every normalized schema node", () => { + const normalized = normalizeSchemaForTypeScript({ + type: "object", + "x-opaque-json": true, + properties: { + json: { "x-opaque-json": true }, + inProcess: { "x-opaque-in-process": true }, + }, + additionalProperties: { "x-opaque-in-process": true }, + }) as Record; + + const assertMarkersRemoved = (value: unknown): void => { + if (Array.isArray(value)) { + value.forEach(assertMarkersRemoved); + } else if (value && typeof value === "object") { + for (const [key, child] of Object.entries(value as Record)) { + expect(key).not.toBe("x-opaque-json"); + expect(key).not.toBe("x-opaque-in-process"); + assertMarkersRemoved(child); + } + } + }; + + assertMarkersRemoved(normalized); + }); }); describe("filterPublicSessionEventVariants", () => { diff --git a/scripts/codegen/typescript.ts b/scripts/codegen/typescript.ts index 30cefd3e9..0c6866ed6 100644 --- a/scripts/codegen/typescript.ts +++ b/scripts/codegen/typescript.ts @@ -40,7 +40,9 @@ import { isSchemaInternal, appendPropertyMarkerTagsToDescriptions, getEnumValueDescriptions, - stripOpaqueJsonMarker, + isBareSchemaNode, + isOpaqueInProcess, + isOpaqueJson, loadSchemaJson, fixBrandCasing, type ApiSchema, @@ -52,6 +54,35 @@ const TS_EXPERIMENTAL_JSDOC = "/** @experimental */"; const EXTERNAL_SCHEMA_TS_IMPORT: Record = { "session-events.schema.json": "./session-events.js", }; +type OpaqueTypeAlias = "JsonValue" | "OpaqueInProcessValue"; + +function opaqueTypeAliasBlock(aliases: ReadonlySet): string { + const declarations: string[] = []; + if (aliases.has("JsonValue")) { + declarations.push( + `/** A value that can be represented losslessly on the SDK JSON wire. */ +export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };` + ); + } + if (aliases.has("OpaqueInProcessValue")) { + declarations.push( + `/** + * A value that lives only in this process and never crosses the JSON-RPC + * boundary, such as a callback or a host object handle. + * @internal + */ +export type OpaqueInProcessValue = unknown;` + ); + } + return declarations.join("\n\n"); +} + +function restoreOpaqueTypeAliasFormatting(code: string): string { + return code.replace( + "export type JsonValue = null | boolean | number | string | JsonValue[] | {[key: string]: JsonValue};", + "export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };" + ); +} function tsExperimentalJSDoc(indent = ""): string { return `${indent}${TS_EXPERIMENTAL_JSDOC}`; @@ -327,7 +358,10 @@ function collectRpcMethods(node: Record): RpcMethod[] { return results; } -export function normalizeSchemaForTypeScript(schema: JSONSchema7): JSONSchema7 { +export function normalizeSchemaForTypeScript( + schema: JSONSchema7, + opaqueTypeAliases?: Set +): JSONSchema7 { const root = structuredClone(schema) as JSONSchema7 & { definitions?: Record; $defs?: Record; @@ -361,12 +395,17 @@ export function normalizeSchemaForTypeScript(schema: JSONSchema7): JSONSchema7 { Object.entries(value as Record).map(([key, child]) => [key, rewrite(child)]) ) as Record; - // The TypeScript codegen doesn't distinguish opaque JSON from any - // other unconstrained value, so drop the marker before feeding the - // schema to json-schema-to-typescript. C# codegen reads the marker - // from its own (un-normalized) view of the schema and emits - // `JsonElement` instead. - stripOpaqueJsonMarker(rewritten); + if (isBareSchemaNode(rewritten as JSONSchema7)) { + if (isOpaqueJson(rewritten as JSONSchema7)) { + rewritten.tsType = "JsonValue"; + opaqueTypeAliases?.add("JsonValue"); + } else if (isOpaqueInProcess(rewritten as JSONSchema7)) { + rewritten.tsType = "OpaqueInProcessValue"; + opaqueTypeAliases?.add("OpaqueInProcessValue"); + } + } + delete rewritten["x-opaque-json"]; + delete rewritten["x-opaque-in-process"]; const enumValueDescriptions = getEnumValueDescriptions(rewritten as JSONSchema7); if (enumValueDescriptions && Array.isArray(rewritten.enum) && rewritten.enum.every((entry) => typeof entry === "string")) { @@ -493,15 +532,23 @@ async function generateSessionEvents(schemaPath?: string): Promise { ); appendPropertyMarkerTagsToDescriptions(schemaForCompile); - const ts = await compile(normalizeSchemaForTypeScript(schemaForCompile), "SessionEvent", { - bannerComment: `/** + const opaqueTypeAliases = new Set(); + const ts = restoreOpaqueTypeAliasFormatting( + await compile(normalizeSchemaForTypeScript(schemaForCompile, opaqueTypeAliases), "SessionEvent", { + bannerComment: [ + `/** * AUTO-GENERATED FILE - DO NOT EDIT * Generated from: session-events.schema.json */`, - style: { semi: true, singleQuote: false, trailingComma: "all" }, - additionalProperties: false, - strictIndexSignatures: true, - }); + opaqueTypeAliasBlock(opaqueTypeAliases), + ] + .filter(Boolean) + .join("\n\n"), + style: { semi: true, singleQuote: false, trailingComma: "all" }, + additionalProperties: false, + strictIndexSignatures: true, + }) + ); let annotatedTs = annotateTypeScriptTypes(ts, experimentalDefinitionNames(definitionCollections), TS_EXPERIMENTAL_JSDOC); // Add @internal JSDoc annotations for session-event types marked @@ -659,6 +706,7 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; if (externalSchemaRefs.size > 0) { lines.push(""); } + const aliasInsertIndex = lines.length; const allMethods = [...collectRpcMethods(schema.server || {}), ...collectRpcMethods(schema.session || {})]; const clientSessionMethods = collectRpcMethods(schema.clientSession || {}); @@ -761,12 +809,17 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; const schemaForCompile = combinedSchema; appendPropertyMarkerTagsToDescriptions(schemaForCompile); - const compiled = await compile(normalizeSchemaForTypeScript(schemaForCompile), "_RpcSchemaRoot", { + const opaqueTypeAliases = new Set(); + const compiled = await compile(normalizeSchemaForTypeScript(schemaForCompile, opaqueTypeAliases), "_RpcSchemaRoot", { bannerComment: "", additionalProperties: false, strictIndexSignatures: true, unreachableDefinitions: true, }); + const aliases = opaqueTypeAliasBlock(opaqueTypeAliases); + if (aliases) { + lines.splice(aliasInsertIndex, 0, aliases, ""); + } // Strip the placeholder root type and keep only the definition-generated types const strippedTs = compiled diff --git a/scripts/codegen/utils.ts b/scripts/codegen/utils.ts index 42e78b9a0..1804990ae 100644 --- a/scripts/codegen/utils.ts +++ b/scripts/codegen/utils.ts @@ -943,6 +943,34 @@ export function isOpaqueJson(schema: JSONSchema7 | null | undefined): boolean { return typeof schema === "object" && schema !== null && (schema as Record)["x-opaque-json"] === true; } +/** Returns true when a JSON Schema node is marked `x-opaque-in-process: true`. */ +export function isOpaqueInProcess(schema: JSONSchema7 | null | undefined): boolean { + return typeof schema === "object" && schema !== null && (schema as Record)["x-opaque-in-process"] === true; +} + +/** + * Returns true when a schema node has no structural constraints that describe a + * more precise TypeScript type than an opaque marker. + */ +export function isBareSchemaNode(schema: JSONSchema7 | null | undefined): boolean { + if (typeof schema !== "object" || schema === null) return false; + const node = schema as Record; + return ![ + "type", + "anyOf", + "oneOf", + "allOf", + "$ref", + "properties", + "items", + "enum", + "const", + "additionalProperties", + "not", + "patternProperties", + ].some((key) => key in node); +} + /** * Removes the `x-opaque-json` marker from a schema node in place. Useful for * codegens (e.g. TypeScript) that don't distinguish opaque JSON from any other From a6d3ed1c844b39fad52af345b0ca401611becfbf Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 12 Aug 2026 13:52:37 -0400 Subject: [PATCH 54/72] Add rewind support across all SDKs (#2321) * Add rewind support across SDKs Expose file change tracking on create and resume session options in all six SDKs, and add shared replay coverage that verifies conversation-and-file rewind behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 91a7e2ab-88a5-4365-accf-cbea1e1391e9 * Fix rewind E2E formatting Apply the Java, Python, and Rust formatters required by CI. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 91a7e2ab-88a5-4365-accf-cbea1e1391e9 * Normalize rewind paths on Windows Treat both Windows path separators as equivalent when validating replayed rewind paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 91a7e2ab-88a5-4365-accf-cbea1e1391e9 * Harden rewind test path construction Use Path.Join so a rooted file name cannot discard the E2E working directory. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 91a7e2ab-88a5-4365-accf-cbea1e1391e9 --------- Copilot-Session: 91a7e2ab-88a5-4365-accf-cbea1e1391e9 --- dotnet/src/Client.cs | 4 + dotnet/src/Types.cs | 12 ++ dotnet/test/E2E/RewindE2ETests.cs | 81 ++++++++++ dotnet/test/Unit/CloneTests.cs | 2 + dotnet/test/Unit/SerializationTests.cs | 4 + go/client.go | 2 + go/client_test.go | 22 ++- go/internal/e2e/rewind_e2e_test.go | 152 ++++++++++++++++++ go/types.go | 9 ++ .../github/copilot/SessionRequestBuilder.java | 2 + .../copilot/rpc/CreateSessionRequest.java | 18 +++ .../copilot/rpc/ResumeSessionConfig.java | 37 +++++ .../copilot/rpc/ResumeSessionRequest.java | 18 +++ .../com/github/copilot/rpc/SessionConfig.java | 36 +++++ .../com/github/copilot/ConfigCloneTest.java | 6 +- .../copilot/SessionRequestBuilderTest.java | 6 +- .../java/com/github/copilot/e2e/RewindIT.java | 128 +++++++++++++++ nodejs/src/client.ts | 2 + nodejs/src/types.ts | 8 + nodejs/test/client.test.ts | 4 + nodejs/test/e2e/rewind.e2e.test.ts | 81 ++++++++++ python/copilot/client.py | 11 ++ python/e2e/test_rewind_e2e.py | 88 ++++++++++ python/test_client.py | 4 + rust/src/types.rs | 33 ++++ rust/src/wire.rs | 4 + rust/tests/e2e.rs | 2 + rust/tests/e2e/rewind.rs | 131 +++++++++++++++ rust/tests/session_test.rs | 4 + ...restore_tracked_file_and_conversation.yaml | 21 +++ 30 files changed, 920 insertions(+), 12 deletions(-) create mode 100644 dotnet/test/E2E/RewindE2ETests.cs create mode 100644 go/internal/e2e/rewind_e2e_test.go create mode 100644 java/src/test/java/com/github/copilot/e2e/RewindIT.java create mode 100644 nodejs/test/e2e/rewind.e2e.test.ts create mode 100644 python/e2e/test_rewind_e2e.py create mode 100644 rust/tests/e2e/rewind.rs create mode 100644 test/snapshots/rewind/should_restore_tracked_file_and_conversation.yaml diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 2df1f05d1..8116be28e 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1141,6 +1141,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.ContextTier, config.Tools?.Select(ToolDefinition.FromAIFunction).ToList(), config.EnableCitations, + config.EnableFileChangeTracking, wireSystemMessage, toolFilter.AvailableTools, toolFilter.ExcludedTools, @@ -1360,6 +1361,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.ContextTier, config.Tools?.Select(ToolDefinition.FromAIFunction).ToList(), config.EnableCitations, + config.EnableFileChangeTracking, wireSystemMessage, toolFilter.AvailableTools, toolFilter.ExcludedTools, @@ -2719,6 +2721,7 @@ internal record CreateSessionRequest( ContextTier? ContextTier, IList? Tools, bool? EnableCitations, + bool? EnableFileChangeTracking, SystemMessageConfig? SystemMessage, IList? AvailableTools, IList? ExcludedTools, @@ -2832,6 +2835,7 @@ internal record ResumeSessionRequest( ContextTier? ContextTier, IList? Tools, bool? EnableCitations, + bool? EnableFileChangeTracking, SystemMessageConfig? SystemMessage, IList? AvailableTools, IList? ExcludedTools, diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 680955a01..59f892140 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -3136,6 +3136,7 @@ protected SessionConfigBase(SessionConfigBase? other) DisabledSkills = other.DisabledSkills is not null ? [.. other.DisabledSkills] : null; DisabledMcpServers = other.DisabledMcpServers is not null ? [.. other.DisabledMcpServers] : null; EnableCitations = other.EnableCitations; + EnableFileChangeTracking = other.EnableFileChangeTracking; EnableConfigDiscovery = other.EnableConfigDiscovery; SkipEmbeddingRetrieval = other.SkipEmbeddingRetrieval; EmbeddingCacheStorage = other.EmbeddingCacheStorage; @@ -3263,6 +3264,17 @@ protected SessionConfigBase(SessionConfigBase? other) [Experimental(Diagnostics.Experimental)] public bool? EnableCitations { get; set; } + /// + /// Opts in to capturing file changes for session rewind and cumulative + /// session diff. + /// + /// + /// On create, capture starts with the first turn. On resume, tracking can be + /// enabled only when the session still has a valid baseline; earlier untracked + /// changes cannot be reconstructed. + /// + public bool? EnableFileChangeTracking { get; set; } + /// /// Override the default configuration directory location. /// When specified, the session will use this directory for storing config and state. diff --git a/dotnet/test/E2E/RewindE2ETests.cs b/dotnet/test/E2E/RewindE2ETests.cs new file mode 100644 index 000000000..ced06b93f --- /dev/null +++ b/dotnet/test/E2E/RewindE2ETests.cs @@ -0,0 +1,81 @@ +// Copyright (c) GitHub, Inc. +// Licensed under the MIT License. + +using GitHub.Copilot.Rpc; +using GitHub.Copilot.Test.Harness; + +using Xunit; +using Xunit.Abstractions; + +namespace GitHub.Copilot.Test.E2E; + +public class RewindE2ETests(E2ETestFixture fixture, ITestOutputHelper output) + : E2ETestBase(fixture, "rewind", output) +{ + private const string FileName = "rewind-sdk.txt"; + private const string FileContent = "SDK rewind content"; + + [Fact] + public async Task Should_Restore_Tracked_File_And_Conversation() + { + var filePath = Path.Join(Ctx.WorkDir, FileName); + await using var session = await CreateSessionAsync(new SessionConfig + { + Model = "claude-sonnet-4.5", + EnableFileChangeTracking = true, + }); + + var response = await session.SendAndWaitAsync( + new MessageOptions + { + Prompt = $"Use the create tool to create {FileName} containing exactly {FileContent}. " + + "After the tool succeeds, reply with exactly SDK_REWIND_DONE.", + }, + TimeSpan.FromSeconds(30)); + + Assert.Equal("SDK_REWIND_DONE", response?.Data.Content); + Assert.True(File.Exists(filePath)); + Assert.Equal(FileContent, await File.ReadAllTextAsync(filePath)); + + HistoryListRewindPointsResult? rewindPoints = null; + await TestHelper.WaitForConditionAsync( + async () => + { + rewindPoints = await session.Rpc.History.ListRewindPointsAsync(); + return rewindPoints.UnavailableReason is null; + }, + timeout: TimeSpan.FromSeconds(10), + timeoutMessage: "Timed out waiting for rewind points to become available.", + pollInterval: TimeSpan.FromMilliseconds(100)); + + Assert.NotNull(rewindPoints); + Assert.True(rewindPoints.FileChangeTrackingEnabled); + var rewindPoint = Assert.Single(rewindPoints.Points); + Assert.True(rewindPoint.CanRestoreFiles); + Assert.Equal(1, rewindPoint.FileCount); + + var preview = await session.Rpc.History.PreviewRewindAsync(rewindPoint.EventId); + Assert.True(preview.Available); + var previewFile = Assert.Single(preview.Files); + Assert.Equal( + Path.GetFullPath(filePath), + Path.GetFullPath(previewFile.Path), + OperatingSystem.IsWindows() ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); + + var rewind = await session.Rpc.History.RewindAsync( + rewindPoint.EventId, + HistoryRewindMode.ConversationAndFiles); + + Assert.Equal(HistoryRewindOutcome.Success, rewind.Outcome); + Assert.True(rewind.EventsRemoved > 0); + var restoredFile = Assert.Single(rewind.RestoredFiles); + Assert.Equal( + Path.GetFullPath(filePath), + Path.GetFullPath(restoredFile), + OperatingSystem.IsWindows() ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); + Assert.False(File.Exists(filePath)); + + var events = await session.GetEventsAsync(); + Assert.DoesNotContain(events, sessionEvent => sessionEvent.Id.ToString() == rewindPoint.EventId); + } +} diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index 88c653283..844eca135 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -78,6 +78,7 @@ public void SessionConfig_Clone_CopiesAllProperties() AdditionalDirectories = ["/shared", "/generated"], Streaming = true, EnableCitations = true, + EnableFileChangeTracking = true, EnableSessionTelemetry = false, EnableExperimentalMode = true, EnableOnDemandInstructionDiscovery = true, @@ -125,6 +126,7 @@ public void SessionConfig_Clone_CopiesAllProperties() Assert.Equal(original.AdditionalDirectories, clone.AdditionalDirectories); Assert.Equal(original.Streaming, clone.Streaming); Assert.Equal(original.EnableCitations, clone.EnableCitations); + Assert.Equal(original.EnableFileChangeTracking, clone.EnableFileChangeTracking); Assert.Equal(original.EnableSessionTelemetry, clone.EnableSessionTelemetry); Assert.Equal(original.EnableExperimentalMode, clone.EnableExperimentalMode); Assert.Equal(original.EnableOnDemandInstructionDiscovery, clone.EnableOnDemandInstructionDiscovery); diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index 9bccf3d77..6edf16809 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -483,6 +483,7 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO createRequestType, ("SessionId", "session-id"), ("EnableCitations", true), + ("EnableFileChangeTracking", true), ("ExcludedBuiltInAgents", excludedAgents), ("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 12.5 })); @@ -490,6 +491,7 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO using var createDocument = JsonDocument.Parse(createJson); var createRoot = createDocument.RootElement; Assert.True(createRoot.GetProperty("enableCitations").GetBoolean()); + Assert.True(createRoot.GetProperty("enableFileChangeTracking").GetBoolean()); Assert.Equal("explore", createRoot.GetProperty("excludedBuiltinAgents")[0].GetString()); Assert.Equal(12.5, createRoot.GetProperty("sessionLimits").GetProperty("maxAiCredits").GetDouble()); @@ -498,6 +500,7 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO resumeRequestType, ("SessionId", "session-id"), ("EnableCitations", true), + ("EnableFileChangeTracking", true), ("ExcludedBuiltInAgents", excludedAgents), ("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 7.25 })); @@ -505,6 +508,7 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO using var resumeDocument = JsonDocument.Parse(resumeJson); var resumeRoot = resumeDocument.RootElement; Assert.True(resumeRoot.GetProperty("enableCitations").GetBoolean()); + Assert.True(resumeRoot.GetProperty("enableFileChangeTracking").GetBoolean()); Assert.Equal("task", resumeRoot.GetProperty("excludedBuiltinAgents")[1].GetString()); Assert.Equal(7.25, resumeRoot.GetProperty("sessionLimits").GetProperty("maxAiCredits").GetDouble()); } diff --git a/go/client.go b/go/client.go index 856e933ea..7d9b9e6bf 100644 --- a/go/client.go +++ b/go/client.go @@ -799,6 +799,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.Models = config.Models req.EnableSessionTelemetry = config.EnableSessionTelemetry req.EnableCitations = config.EnableCitations + req.EnableFileChangeTracking = config.EnableFileChangeTracking req.SessionLimits = config.SessionLimits req.IsExperimentalMode = config.EnableExperimentalMode req.SkipCustomInstructions = config.SkipCustomInstructions @@ -1148,6 +1149,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.ToolFilterPrecedence = precedence req.ExcludedBuiltInAgents = config.ExcludedBuiltInAgents req.EnableCitations = config.EnableCitations + req.EnableFileChangeTracking = config.EnableFileChangeTracking req.SessionLimits = config.SessionLimits if config.Streaming != nil { req.Streaming = config.Streaming diff --git a/go/client_test.go b/go/client_test.go index 3322d7741..8152fb469 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -398,14 +398,15 @@ func TestClient_ForwardsNewSessionOptionsToSessionRequests(t *testing.T) { }) _, err := client.CreateSession(t.Context(), &SessionConfig{ - ExcludedBuiltInAgents: []string{"explore"}, - EnableCitations: Bool(true), - SessionLimits: &rpc.SessionLimitsConfig{MaxAiCredits: float64Ptr(30)}, + ExcludedBuiltInAgents: []string{"explore"}, + EnableCitations: Bool(true), + EnableFileChangeTracking: Bool(true), + SessionLimits: &rpc.SessionLimitsConfig{MaxAiCredits: float64Ptr(30)}, }) if err != nil { t.Fatalf("CreateSession failed: %v", err) } - assertNewSessionOptions(t, <-createParams, true, "explore", 30) + assertNewSessionOptions(t, <-createParams, true, true, "explore", 30) resumeParams := make(chan json.RawMessage, 1) server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { @@ -414,14 +415,15 @@ func TestClient_ForwardsNewSessionOptionsToSessionRequests(t *testing.T) { }) _, err = client.ResumeSessionWithOptions(t.Context(), "resumed-options", &ResumeSessionConfig{ - ExcludedBuiltInAgents: []string{"task"}, - EnableCitations: Bool(false), - SessionLimits: &rpc.SessionLimitsConfig{MaxAiCredits: float64Ptr(15)}, + ExcludedBuiltInAgents: []string{"task"}, + EnableCitations: Bool(false), + EnableFileChangeTracking: Bool(false), + SessionLimits: &rpc.SessionLimitsConfig{MaxAiCredits: float64Ptr(15)}, }) if err != nil { t.Fatalf("ResumeSessionWithOptions failed: %v", err) } - assertNewSessionOptions(t, <-resumeParams, false, "task", 15) + assertNewSessionOptions(t, <-resumeParams, false, false, "task", 15) } func assertCapiEnableWebSocketResponses(t *testing.T, params json.RawMessage) { @@ -445,6 +447,7 @@ func assertNewSessionOptions( t *testing.T, params json.RawMessage, expectedCitations bool, + expectedFileChangeTracking bool, expectedAgent string, expectedCredits float64, ) { @@ -457,6 +460,9 @@ func assertNewSessionOptions( if decoded["enableCitations"] != expectedCitations { t.Fatalf("expected enableCitations=%v, got %v", expectedCitations, decoded["enableCitations"]) } + if decoded["enableFileChangeTracking"] != expectedFileChangeTracking { + t.Fatalf("expected enableFileChangeTracking=%v, got %v", expectedFileChangeTracking, decoded["enableFileChangeTracking"]) + } agents, ok := decoded["excludedBuiltinAgents"].([]any) if !ok || len(agents) != 1 || agents[0] != expectedAgent { t.Fatalf("expected excludedBuiltinAgents=[%q], got %#v", expectedAgent, decoded["excludedBuiltinAgents"]) diff --git a/go/internal/e2e/rewind_e2e_test.go b/go/internal/e2e/rewind_e2e_test.go new file mode 100644 index 000000000..b15e546eb --- /dev/null +++ b/go/internal/e2e/rewind_e2e_test.go @@ -0,0 +1,152 @@ +package e2e + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/internal/e2e/testharness" + "github.com/github/copilot-sdk/go/rpc" +) + +const ( + rewindFileName = "rewind-sdk.txt" + rewindFileContent = "SDK rewind content" +) + +func TestRewindE2E(t *testing.T) { + ctx := testharness.NewTestContext(t) + client := ctx.NewClient() + t.Cleanup(func() { client.ForceStop() }) + + t.Run("should restore tracked file and conversation", func(t *testing.T) { + ctx.ConfigureForTest(t) + filePath := filepath.Join(ctx.WorkDir, rewindFileName) + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + Model: "claude-sonnet-4.5", + EnableFileChangeTracking: copilot.Bool(true), + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + defer session.Disconnect() + + response, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the create tool to create " + rewindFileName + " containing exactly " + + rewindFileContent + ". After the tool succeeds, reply with exactly SDK_REWIND_DONE.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + responseData, ok := response.Data.(*copilot.AssistantMessageData) + if !ok || responseData.Content != "SDK_REWIND_DONE" { + t.Fatalf("Expected SDK_REWIND_DONE response, got %+v", response) + } + content, err := os.ReadFile(filePath) + if err != nil { + t.Fatalf("Failed to read created file: %v", err) + } + if string(content) != rewindFileContent { + t.Fatalf("Expected file content %q, got %q", rewindFileContent, content) + } + + rewindPoints := waitForRewindPoints(t, session) + if !rewindPoints.FileChangeTrackingEnabled { + t.Fatal("Expected file change tracking to be enabled") + } + if len(rewindPoints.Points) != 1 { + t.Fatalf("Expected one rewind point, got %+v", rewindPoints.Points) + } + rewindPoint := rewindPoints.Points[0] + if !rewindPoint.CanRestoreFiles || rewindPoint.FileCount != 1 { + t.Fatalf("Expected one restorable file, got %+v", rewindPoint) + } + + preview, err := session.RPC.History.PreviewRewind(t.Context(), &rpc.HistoryPreviewRewindRequest{ + EventID: rewindPoint.EventID, + }) + if err != nil { + t.Fatalf("PreviewRewind failed: %v", err) + } + if !preview.Available || len(preview.Files) != 1 { + t.Fatalf("Expected one available preview file, got %+v", preview) + } + assertSameRewindPath(t, filePath, preview.Files[0].Path) + + rewind, err := session.RPC.History.Rewind(t.Context(), &rpc.HistoryRewindRequest{ + EventID: rewindPoint.EventID, + Mode: rpc.HistoryRewindModeConversationAndFiles, + }) + if err != nil { + t.Fatalf("Rewind failed: %v", err) + } + if rewind.Outcome != rpc.HistoryRewindOutcomeSuccess { + t.Fatalf("Expected successful rewind, got %+v", rewind) + } + if rewind.EventsRemoved == nil || *rewind.EventsRemoved < 1 { + t.Fatalf("Expected rewind to remove events, got %+v", rewind) + } + if len(rewind.RestoredFiles) != 1 { + t.Fatalf("Expected one restored file, got %+v", rewind.RestoredFiles) + } + assertSameRewindPath(t, filePath, rewind.RestoredFiles[0]) + if _, err := os.Stat(filePath); !os.IsNotExist(err) { + t.Fatalf("Expected rewound file to be removed, stat error: %v", err) + } + + events, err := session.GetEvents(t.Context()) + if err != nil { + t.Fatalf("GetEvents failed: %v", err) + } + for _, event := range events { + if event.ID == rewindPoint.EventID { + t.Fatalf("Expected rewound event %q to be removed", rewindPoint.EventID) + } + } + }) +} + +func waitForRewindPoints(t *testing.T, session *copilot.Session) *rpc.HistoryListRewindPointsResult { + t.Helper() + deadline := time.Now().Add(10 * time.Second) + for { + result, err := session.RPC.History.ListRewindPoints(t.Context()) + if err != nil { + t.Fatalf("ListRewindPoints failed: %v", err) + } + if result.UnavailableReason == nil { + return result + } + if time.Now().After(deadline) { + t.Fatalf("Timed out waiting for rewind points: %s", *result.UnavailableReason) + } + time.Sleep(100 * time.Millisecond) + } +} + +func assertSameRewindPath(t *testing.T, expected, actual string) { + t.Helper() + expectedPath, err := filepath.Abs(expected) + if err != nil { + t.Fatalf("Failed to resolve expected path: %v", err) + } + actualPath, err := filepath.Abs(actual) + if err != nil { + t.Fatalf("Failed to resolve actual path: %v", err) + } + + expectedPath = filepath.Clean(expectedPath) + actualPath = filepath.Clean(actualPath) + if runtime.GOOS == "windows" { + if !strings.EqualFold(expectedPath, actualPath) { + t.Fatalf("Expected path %q, got %q", expectedPath, actualPath) + } + } else if expectedPath != actualPath { + t.Fatalf("Expected path %q, got %q", expectedPath, actualPath) + } +} diff --git a/go/types.go b/go/types.go index 6d6a877d3..8690695f2 100644 --- a/go/types.go +++ b/go/types.go @@ -1326,6 +1326,9 @@ type SessionConfig struct { // Experimental: EnableCitations is part of an experimental model capability // surface and may change or be removed in future SDK or CLI releases. EnableCitations *bool + // EnableFileChangeTracking opts in to capturing file changes from the first + // turn for session rewind and cumulative session diff. + EnableFileChangeTracking *bool // SessionLimits applies limits to this session's current accounting window. // // Experimental: SessionLimits is part of an experimental runtime accounting @@ -1790,6 +1793,10 @@ type ResumeSessionConfig struct { // Experimental: EnableCitations is part of an experimental model capability // surface and may change or be removed in future SDK or CLI releases. EnableCitations *bool + // EnableFileChangeTracking opts in to capturing file changes for session + // rewind and cumulative session diff when the resumed session has a valid + // baseline. Earlier untracked changes cannot be reconstructed. + EnableFileChangeTracking *bool // SessionLimits applies limits to this session's current accounting window. // // Experimental: SessionLimits is part of an experimental runtime accounting @@ -2416,6 +2423,7 @@ type createSessionRequest struct { Models []ProviderModelConfig `json:"models,omitempty"` EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` EnableCitations *bool `json:"enableCitations,omitempty"` + EnableFileChangeTracking *bool `json:"enableFileChangeTracking,omitempty"` SessionLimits *rpc.SessionLimitsConfig `json:"sessionLimits,omitempty"` IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` @@ -2511,6 +2519,7 @@ type resumeSessionRequest struct { Models []ProviderModelConfig `json:"models,omitempty"` EnableSessionTelemetry *bool `json:"enableSessionTelemetry,omitempty"` EnableCitations *bool `json:"enableCitations,omitempty"` + EnableFileChangeTracking *bool `json:"enableFileChangeTracking,omitempty"` SessionLimits *rpc.SessionLimitsConfig `json:"sessionLimits,omitempty"` IsExperimentalMode *bool `json:"isExperimentalMode,omitempty"` SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java index 23e4f77b4..4254c04ec 100644 --- a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -130,6 +130,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setModels(config.getModels()); config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); config.getEnableCitations().ifPresent(request::setEnableCitations); + config.getEnableFileChangeTracking().ifPresent(request::setEnableFileChangeTracking); request.setSessionLimits(config.getSessionLimits()); experimentalModeForMode(mode, config.getEnableExperimentalMode().orElse(null)) .ifPresent(request::setIsExperimentalMode); @@ -265,6 +266,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setModels(config.getModels()); config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); config.getEnableCitations().ifPresent(request::setEnableCitations); + config.getEnableFileChangeTracking().ifPresent(request::setEnableFileChangeTracking); request.setSessionLimits(config.getSessionLimits()); experimentalModeForMode(mode, config.getEnableExperimentalMode().orElse(null)) .ifPresent(request::setIsExperimentalMode); diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java index 4c74e38ac..2eab977db 100644 --- a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java @@ -80,6 +80,9 @@ public final class CreateSessionRequest { @JsonProperty("enableCitations") private Boolean enableCitations; + @JsonProperty("enableFileChangeTracking") + private Boolean enableFileChangeTracking; + @JsonProperty("sessionLimits") private SessionLimitsConfig sessionLimits; @@ -434,6 +437,21 @@ public void setEnableCitations(boolean enableCitations) { this.enableCitations = enableCitations; } + /** Gets the file change tracking flag. @return the flag */ + public Boolean getEnableFileChangeTracking() { + return enableFileChangeTracking; + } + + /** + * Sets the file change tracking flag. + * + * @param enableFileChangeTracking + * the flag + */ + public void setEnableFileChangeTracking(boolean enableFileChangeTracking) { + this.enableFileChangeTracking = enableFileChangeTracking; + } + /** Gets the session limits. @return the session limits */ public SessionLimitsConfig getSessionLimits() { return sessionLimits; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java index 10641157b..a18803637 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java @@ -53,6 +53,7 @@ public class ResumeSessionConfig { private List models; private Boolean enableSessionTelemetry; private Boolean enableCitations; + private Boolean enableFileChangeTracking; private SessionLimitsConfig sessionLimits; private Boolean enableExperimentalMode; private Boolean skipCustomInstructions; @@ -453,6 +454,41 @@ public ResumeSessionConfig clearEnableCitations() { return this; } + /** + * Gets whether file change tracking is enabled for rewind and cumulative + * session diff. + * + * @return an {@link java.util.Optional} containing the setting, or + * {@link java.util.Optional#empty()} for the default + */ + @JsonIgnore + public Optional getEnableFileChangeTracking() { + return Optional.ofNullable(enableFileChangeTracking); + } + + /** + * Enables or disables file change tracking when the resumed session has a valid + * baseline. Earlier untracked changes cannot be reconstructed. + * + * @param enableFileChangeTracking + * whether to enable file change tracking + * @return this config instance for method chaining + */ + public ResumeSessionConfig setEnableFileChangeTracking(boolean enableFileChangeTracking) { + this.enableFileChangeTracking = enableFileChangeTracking; + return this; + } + + /** + * Clears the file change tracking setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableFileChangeTracking() { + this.enableFileChangeTracking = null; + return this; + } + /** * Gets the limits for this session's current accounting window. * @@ -1958,6 +1994,7 @@ public ResumeSessionConfig clone() { copy.models = this.models != null ? new ArrayList<>(this.models) : null; copy.enableSessionTelemetry = this.enableSessionTelemetry; copy.enableCitations = this.enableCitations; + copy.enableFileChangeTracking = this.enableFileChangeTracking; copy.sessionLimits = this.sessionLimits; copy.enableExperimentalMode = this.enableExperimentalMode; copy.reasoningEffort = this.reasoningEffort; diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java index 8c9d03ede..e52892477 100644 --- a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java +++ b/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java @@ -82,6 +82,9 @@ public final class ResumeSessionRequest { @JsonProperty("enableCitations") private Boolean enableCitations; + @JsonProperty("enableFileChangeTracking") + private Boolean enableFileChangeTracking; + @JsonProperty("sessionLimits") private SessionLimitsConfig sessionLimits; @@ -439,6 +442,21 @@ public void setEnableCitations(boolean enableCitations) { this.enableCitations = enableCitations; } + /** Gets the file change tracking flag. @return the flag */ + public Boolean getEnableFileChangeTracking() { + return enableFileChangeTracking; + } + + /** + * Sets the file change tracking flag. + * + * @param enableFileChangeTracking + * the flag + */ + public void setEnableFileChangeTracking(boolean enableFileChangeTracking) { + this.enableFileChangeTracking = enableFileChangeTracking; + } + /** Gets the session limits. @return the session limits */ public SessionLimitsConfig getSessionLimits() { return sessionLimits; diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java index 3ccda690f..1127e6777 100644 --- a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java +++ b/java/src/main/java/com/github/copilot/rpc/SessionConfig.java @@ -57,6 +57,7 @@ public class SessionConfig { private List models; private Boolean enableSessionTelemetry; private Boolean enableCitations; + private Boolean enableFileChangeTracking; private SessionLimitsConfig sessionLimits; private Boolean enableExperimentalMode; private Boolean skipCustomInstructions; @@ -556,6 +557,40 @@ public SessionConfig clearEnableCitations() { return this; } + /** + * Gets whether file change tracking is enabled for rewind and cumulative + * session diff. + * + * @return an {@link java.util.Optional} containing the setting, or + * {@link java.util.Optional#empty()} for the default + */ + @JsonIgnore + public Optional getEnableFileChangeTracking() { + return Optional.ofNullable(enableFileChangeTracking); + } + + /** + * Enables or disables file change tracking from the first turn. + * + * @param enableFileChangeTracking + * whether to enable file change tracking + * @return this config instance for method chaining + */ + public SessionConfig setEnableFileChangeTracking(boolean enableFileChangeTracking) { + this.enableFileChangeTracking = enableFileChangeTracking; + return this; + } + + /** + * Clears the file change tracking setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableFileChangeTracking() { + this.enableFileChangeTracking = null; + return this; + } + /** * Gets the limits for this session's current accounting window. * @@ -2098,6 +2133,7 @@ public SessionConfig clone() { copy.models = this.models != null ? new ArrayList<>(this.models) : null; copy.enableSessionTelemetry = this.enableSessionTelemetry; copy.enableCitations = this.enableCitations; + copy.enableFileChangeTracking = this.enableFileChangeTracking; copy.sessionLimits = this.sessionLimits; copy.enableExperimentalMode = this.enableExperimentalMode; copy.skipCustomInstructions = this.skipCustomInstructions; diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/src/test/java/com/github/copilot/ConfigCloneTest.java index c3f726ca1..b2b744eed 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -181,13 +181,14 @@ void sessionConfigSessionPolicyOptionsCloned() { var sessionLimits = new SessionLimitsConfig(30.0); var excludedAgents = new ArrayList<>(List.of("explore")); SessionConfig original = new SessionConfig().setExcludedBuiltInAgents(excludedAgents).setEnableCitations(true) - .setSessionLimits(sessionLimits); + .setEnableFileChangeTracking(true).setSessionLimits(sessionLimits); SessionConfig cloned = original.clone(); excludedAgents.add("task"); assertEquals(List.of("explore"), cloned.getExcludedBuiltInAgents()); assertTrue(cloned.getEnableCitations().orElse(false)); + assertTrue(cloned.getEnableFileChangeTracking().orElse(false)); assertSame(sessionLimits, cloned.getSessionLimits()); } @@ -235,13 +236,14 @@ void resumeSessionConfigSessionPolicyOptionsCloned() { var sessionLimits = new SessionLimitsConfig(30.0); var excludedAgents = new ArrayList<>(List.of("explore")); ResumeSessionConfig original = new ResumeSessionConfig().setExcludedBuiltInAgents(excludedAgents) - .setEnableCitations(true).setSessionLimits(sessionLimits); + .setEnableCitations(true).setEnableFileChangeTracking(true).setSessionLimits(sessionLimits); ResumeSessionConfig cloned = original.clone(); excludedAgents.add("task"); assertEquals(List.of("explore"), cloned.getExcludedBuiltInAgents()); assertTrue(cloned.getEnableCitations().orElse(false)); + assertTrue(cloned.getEnableFileChangeTracking().orElse(false)); assertSame(sessionLimits, cloned.getSessionLimits()); } diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java index 329a7500a..0525786de 100644 --- a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -229,12 +229,13 @@ void testBuildCreateRequestForwardsExplicitMcpOAuthTokenStorage() { void testBuildCreateRequestForwardsSessionPolicyOptions() { var sessionLimits = new SessionLimitsConfig(30.0); var config = new SessionConfig().setExcludedBuiltInAgents(List.of("explore")).setEnableCitations(true) - .setSessionLimits(sessionLimits); + .setEnableFileChangeTracking(true).setSessionLimits(sessionLimits); CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config, "session-policy"); assertEquals(List.of("explore"), request.getExcludedBuiltInAgents()); assertTrue(request.getEnableCitations()); + assertTrue(request.getEnableFileChangeTracking()); assertSame(sessionLimits, request.getSessionLimits()); } @@ -434,12 +435,13 @@ void testBuildResumeRequestForwardsExplicitMcpOAuthTokenStorage() { void testBuildResumeRequestForwardsSessionPolicyOptions() { var sessionLimits = new SessionLimitsConfig(30.0); var config = new ResumeSessionConfig().setExcludedBuiltInAgents(List.of("explore")).setEnableCitations(true) - .setSessionLimits(sessionLimits); + .setEnableFileChangeTracking(true).setSessionLimits(sessionLimits); ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-policy", config); assertEquals(List.of("explore"), request.getExcludedBuiltInAgents()); assertTrue(request.getEnableCitations()); + assertTrue(request.getEnableFileChangeTracking()); assertSame(sessionLimits, request.getSessionLimits()); } diff --git a/java/src/test/java/com/github/copilot/e2e/RewindIT.java b/java/src/test/java/com/github/copilot/e2e/RewindIT.java new file mode 100644 index 000000000..eff48dde0 --- /dev/null +++ b/java/src/test/java/com/github/copilot/e2e/RewindIT.java @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.e2e; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.github.copilot.AllowCopilotExperimental; +import com.github.copilot.CopilotClient; +import com.github.copilot.CopilotSession; +import com.github.copilot.E2ETestContext; +import com.github.copilot.generated.AssistantMessageEvent; +import com.github.copilot.generated.rpc.HistoryRewindMode; +import com.github.copilot.generated.rpc.HistoryRewindOutcome; +import com.github.copilot.generated.rpc.SessionHistoryListRewindPointsResult; +import com.github.copilot.generated.rpc.SessionHistoryPreviewRewindParams; +import com.github.copilot.generated.rpc.SessionHistoryRewindParams; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; + +@AllowCopilotExperimental +class RewindIT { + + private static final String FILE_NAME = "rewind-sdk.txt"; + private static final String FILE_CONTENT = "SDK rewind content"; + + private static E2ETestContext ctx; + + @BeforeAll + static void setup() throws Exception { + ctx = E2ETestContext.create(); + } + + @AfterAll + static void teardown() throws Exception { + if (ctx != null) { + ctx.close(); + } + } + + @Test + void shouldRestoreTrackedFileAndConversation() throws Exception { + ctx.configureForTest("rewind", "should_restore_tracked_file_and_conversation"); + Path filePath = ctx.getWorkDir().resolve(FILE_NAME); + + try (CopilotClient client = ctx.createClient(); + CopilotSession session = client + .createSession( + new SessionConfig().setModel("claude-sonnet-4.5").setEnableFileChangeTracking(true) + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS)) { + AssistantMessageEvent response = session + .sendAndWait(new MessageOptions().setPrompt( + "Use the create tool to create " + FILE_NAME + " containing exactly " + FILE_CONTENT + + ". After the tool succeeds, reply with exactly SDK_REWIND_DONE."), + 30_000) + .get(60, TimeUnit.SECONDS); + + assertNotNull(response); + assertEquals("SDK_REWIND_DONE", response.getData().content()); + assertEquals(FILE_CONTENT, Files.readString(filePath)); + + SessionHistoryListRewindPointsResult rewindPoints = waitForRewindPoints(session); + assertTrue(Boolean.TRUE.equals(rewindPoints.fileChangeTrackingEnabled())); + assertEquals(1, rewindPoints.points().size()); + var rewindPoint = rewindPoints.points().get(0); + assertTrue(Boolean.TRUE.equals(rewindPoint.canRestoreFiles())); + assertEquals(1L, rewindPoint.fileCount()); + + var preview = session.getRpc().history + .previewRewind(new SessionHistoryPreviewRewindParams(null, rewindPoint.eventId())) + .get(10, TimeUnit.SECONDS); + assertTrue(Boolean.TRUE.equals(preview.available())); + assertEquals(1, preview.files().size()); + assertSamePath(filePath, preview.files().get(0).path()); + + var rewind = session.getRpc().history.rewind(new SessionHistoryRewindParams(null, rewindPoint.eventId(), + HistoryRewindMode.CONVERSATION_AND_FILES)).get(10, TimeUnit.SECONDS); + assertEquals(HistoryRewindOutcome.SUCCESS, rewind.outcome()); + assertTrue(rewind.eventsRemoved() != null && rewind.eventsRemoved() > 0); + assertEquals(1, rewind.restoredFiles().size()); + assertSamePath(filePath, rewind.restoredFiles().get(0)); + assertFalse(Files.exists(filePath)); + + var events = session.getMessages().get(10, TimeUnit.SECONDS); + assertTrue(events.stream().noneMatch(event -> event.getId().toString().equals(rewindPoint.eventId()))); + } + } + + private static SessionHistoryListRewindPointsResult waitForRewindPoints(CopilotSession session) throws Exception { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); + SessionHistoryListRewindPointsResult result; + do { + result = session.getRpc().history.listRewindPoints().get(10, TimeUnit.SECONDS); + if (result.unavailableReason() == null) { + return result; + } + TimeUnit.MILLISECONDS.sleep(100); + } while (System.nanoTime() < deadline); + + assertNull(result.unavailableReason(), "Timed out waiting for rewind points to become available"); + return result; + } + + private static void assertSamePath(Path expected, String actual) { + String expectedPath = expected.toAbsolutePath().normalize().toString(); + String actualPath = Path.of(actual).toAbsolutePath().normalize().toString(); + if (System.getProperty("os.name").startsWith("Windows")) { + assertTrue(expectedPath.equalsIgnoreCase(actualPath)); + } else { + assertEquals(expectedPath, actualPath); + } + } +} diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index c30b2207b..1182b4106 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1561,6 +1561,7 @@ export class CopilotClient { models: config.models, enableSessionTelemetry: config.enableSessionTelemetry, enableCitations: config.enableCitations, + enableFileChangeTracking: config.enableFileChangeTracking, sessionLimits: config.sessionLimits, modelCapabilities: config.modelCapabilities, largeOutput: toWireLargeOutput(config.largeOutput), @@ -1781,6 +1782,7 @@ export class CopilotClient { enableSessionTelemetry: config.enableSessionTelemetry, excludedBuiltinAgents: config.excludedBuiltinAgents, enableCitations: config.enableCitations, + enableFileChangeTracking: config.enableFileChangeTracking, sessionLimits: config.sessionLimits, tools: config.tools?.map((tool) => ({ name: tool.name, diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 3a5f7714b..2567d7d31 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -2364,6 +2364,14 @@ export interface SessionConfigBase { */ enableCitations?: boolean; + /** + * Opt in to capturing file changes for session rewind and cumulative session + * diff. On create, capture starts with the first turn. On resume, this can + * enable tracking only when the session still has a valid baseline; it cannot + * reconstruct changes from earlier untracked turns. + */ + enableFileChangeTracking?: boolean; + /** * Limits applied to this session's current accounting window. * diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 01a97e980..254c126ed 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -792,12 +792,14 @@ describe("CopilotClient", () => { const session = await client.createSession({ onPermissionRequest: approveAll, enableCitations: true, + enableFileChangeTracking: true, excludedBuiltinAgents: ["explore"], sessionLimits: { maxAiCredits: 30 }, }); await client.resumeSession(session.sessionId, { onPermissionRequest: approveAll, enableCitations: false, + enableFileChangeTracking: false, excludedBuiltinAgents: ["task"], sessionLimits: { maxAiCredits: 15 }, }); @@ -809,9 +811,11 @@ describe("CopilotClient", () => { ([method]) => method === "session.resume" )![1] as any; expect(createPayload.enableCitations).toBe(true); + expect(createPayload.enableFileChangeTracking).toBe(true); expect(createPayload.excludedBuiltinAgents).toEqual(["explore"]); expect(createPayload.sessionLimits).toEqual({ maxAiCredits: 30 }); expect(resumePayload.enableCitations).toBe(false); + expect(resumePayload.enableFileChangeTracking).toBe(false); expect(resumePayload.excludedBuiltinAgents).toEqual(["task"]); expect(resumePayload.sessionLimits).toEqual({ maxAiCredits: 15 }); }); diff --git a/nodejs/test/e2e/rewind.e2e.test.ts b/nodejs/test/e2e/rewind.e2e.test.ts new file mode 100644 index 000000000..920ffed19 --- /dev/null +++ b/nodejs/test/e2e/rewind.e2e.test.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import { existsSync, readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { describe, expect, it } from "vitest"; +import { approveAll } from "../../src/index.js"; +import { createSdkTestContext } from "./harness/sdkTestContext.js"; + +const FILE_NAME = "rewind-sdk.txt"; +const FILE_CONTENT = "SDK rewind content"; + +function expectSamePath(actual: string, expected: string): void { + const actualPath = resolve(actual); + const expectedPath = resolve(expected); + if (process.platform === "win32") { + expect(actualPath.toLowerCase()).toBe(expectedPath.toLowerCase()); + } else { + expect(actualPath).toBe(expectedPath); + } +} + +describe("Rewind", async () => { + const { copilotClient: client, workDir } = await createSdkTestContext(); + + it("should restore tracked file and conversation", async () => { + const filePath = join(workDir, FILE_NAME); + const session = await client.createSession({ + model: "claude-sonnet-4.5", + enableFileChangeTracking: true, + onPermissionRequest: approveAll, + }); + + try { + const response = await session.sendAndWait({ + prompt: `Use the create tool to create ${FILE_NAME} containing exactly ${FILE_CONTENT}. After the tool succeeds, reply with exactly SDK_REWIND_DONE.`, + }); + + expect(response?.data.content).toBe("SDK_REWIND_DONE"); + expect(existsSync(filePath)).toBe(true); + expect(readFileSync(filePath, "utf8")).toBe(FILE_CONTENT); + + let rewindPoints = await session.rpc.history.listRewindPoints(); + const deadline = Date.now() + 10_000; + while (rewindPoints.unavailableReason && Date.now() < deadline) { + await new Promise((resolveDelay) => setTimeout(resolveDelay, 100)); + rewindPoints = await session.rpc.history.listRewindPoints(); + } + + expect(rewindPoints.unavailableReason).toBeUndefined(); + expect(rewindPoints.fileChangeTrackingEnabled).toBe(true); + expect(rewindPoints.points).toHaveLength(1); + const rewindPoint = rewindPoints.points[0]; + expect(rewindPoint.canRestoreFiles).toBe(true); + expect(rewindPoint.fileCount).toBe(1); + + const preview = await session.rpc.history.previewRewind({ + eventId: rewindPoint.eventId, + }); + expect(preview.available).toBe(true); + expect(preview.files).toHaveLength(1); + expectSamePath(preview.files[0].path, filePath); + + const rewind = await session.rpc.history.rewind({ + eventId: rewindPoint.eventId, + mode: "conversation-and-files", + }); + expect(rewind.outcome).toBe("success"); + expect(rewind.eventsRemoved).toBeGreaterThan(0); + expect(rewind.restoredFiles).toHaveLength(1); + expectSamePath(rewind.restoredFiles[0], filePath); + expect(existsSync(filePath)).toBe(false); + + const events = await session.getEvents(); + expect(events.some((event) => event.id === rewindPoint.eventId)).toBe(false); + } finally { + await session.disconnect(); + } + }); +}); diff --git a/python/copilot/client.py b/python/copilot/client.py index 21ceb6eee..415f44ef5 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2094,6 +2094,7 @@ async def create_session( models: list[ProviderModelConfig] | None = None, enable_session_telemetry: bool | None = None, enable_citations: bool | None = None, + enable_file_change_tracking: bool | None = None, excluded_builtin_agents: list[str] | None = None, session_limits: SessionLimitsConfig | None = None, skip_custom_instructions: bool | None = None, @@ -2210,6 +2211,8 @@ async def create_session( OpenTelemetry configuration. enable_citations: **Experimental.** Enables native model citations for supported providers. + enable_file_change_tracking: Opts in to capturing file changes from the + first turn for session rewind and cumulative session diff. excluded_builtin_agents: Built-in agent names to exclude from the session. Excluded built-in agents are hidden from discovery and cannot be selected or invoked unless a custom agent with the same @@ -2500,6 +2503,8 @@ async def create_session( payload["enableSessionTelemetry"] = enable_session_telemetry if enable_citations is not None: payload["enableCitations"] = enable_citations + if enable_file_change_tracking is not None: + payload["enableFileChangeTracking"] = enable_file_change_tracking if excluded_builtin_agents is not None: payload["excludedBuiltinAgents"] = excluded_builtin_agents if session_limits is not None: @@ -2817,6 +2822,7 @@ async def resume_session( models: list[ProviderModelConfig] | None = None, enable_session_telemetry: bool | None = None, enable_citations: bool | None = None, + enable_file_change_tracking: bool | None = None, excluded_builtin_agents: list[str] | None = None, session_limits: SessionLimitsConfig | None = None, skip_custom_instructions: bool | None = None, @@ -2934,6 +2940,9 @@ async def resume_session( OpenTelemetry configuration. enable_citations: **Experimental.** Enables native model citations for supported providers. + enable_file_change_tracking: Opts in to capturing file changes for + session rewind and cumulative session diff when the resumed session + has a valid baseline. Earlier untracked changes cannot be reconstructed. excluded_builtin_agents: Built-in agent names to exclude from the resumed session. Excluded built-in agents are hidden from discovery and cannot be selected or invoked unless a custom agent with the @@ -3139,6 +3148,8 @@ async def resume_session( payload["enableSessionTelemetry"] = enable_session_telemetry if enable_citations is not None: payload["enableCitations"] = enable_citations + if enable_file_change_tracking is not None: + payload["enableFileChangeTracking"] = enable_file_change_tracking if excluded_builtin_agents is not None: payload["excludedBuiltinAgents"] = excluded_builtin_agents if session_limits is not None: diff --git a/python/e2e/test_rewind_e2e.py b/python/e2e/test_rewind_e2e.py new file mode 100644 index 000000000..e3db37915 --- /dev/null +++ b/python/e2e/test_rewind_e2e.py @@ -0,0 +1,88 @@ +"""E2E coverage for rewinding tracked files and conversation history.""" + +from __future__ import annotations + +import asyncio +import os +from pathlib import Path + +import pytest + +from copilot.rpc import ( + HistoryPreviewRewindRequest, + HistoryRewindMode, + HistoryRewindOutcome, + HistoryRewindRequest, +) +from copilot.session import PermissionHandler + +from .testharness import E2ETestContext + +pytestmark = pytest.mark.asyncio(loop_scope="module") + +FILE_NAME = "rewind-sdk.txt" +FILE_CONTENT = "SDK rewind content" + + +def _same_path(left: str | Path, right: str | Path) -> bool: + return os.path.normcase(os.path.abspath(left)) == os.path.normcase(os.path.abspath(right)) + + +class TestRewind: + async def test_should_restore_tracked_file_and_conversation(self, ctx: E2ETestContext): + file_path = Path(ctx.work_dir) / FILE_NAME + session = await ctx.client.create_session( + model="claude-sonnet-4.5", + enable_file_change_tracking=True, + on_permission_request=PermissionHandler.approve_all, + ) + + try: + response = await session.send_and_wait( + f"Use the create tool to create {FILE_NAME} containing exactly {FILE_CONTENT}. " + "After the tool succeeds, reply with exactly SDK_REWIND_DONE." + ) + + assert response is not None + assert response.data.content == "SDK_REWIND_DONE" + assert file_path.read_text(encoding="utf-8") == FILE_CONTENT + + rewind_points = await session.rpc.history.list_rewind_points() + deadline = asyncio.get_running_loop().time() + 10 + while ( + rewind_points.unavailable_reason is not None + and asyncio.get_running_loop().time() < deadline + ): + await asyncio.sleep(0.1) + rewind_points = await session.rpc.history.list_rewind_points() + + assert rewind_points.unavailable_reason is None + assert rewind_points.file_change_tracking_enabled + assert len(rewind_points.points) == 1 + rewind_point = rewind_points.points[0] + assert rewind_point.can_restore_files + assert rewind_point.file_count == 1 + + preview = await session.rpc.history.preview_rewind( + HistoryPreviewRewindRequest(event_id=rewind_point.event_id) + ) + assert preview.available + assert len(preview.files) == 1 + assert _same_path(preview.files[0].path, file_path) + + rewind = await session.rpc.history.rewind( + HistoryRewindRequest( + event_id=rewind_point.event_id, + mode=HistoryRewindMode.CONVERSATION_AND_FILES, + ) + ) + assert rewind.outcome == HistoryRewindOutcome.SUCCESS + assert rewind.events_removed is not None and rewind.events_removed > 0 + assert len(rewind.restored_files) == 1 + assert _same_path(rewind.restored_files[0], file_path) + assert not file_path.exists() + + events = await session.get_events() + assert all(str(event.id) != rewind_point.event_id for event in events) + finally: + await session.disconnect() diff --git a/python/test_client.py b/python/test_client.py index 2375bc98a..893b82af1 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -980,6 +980,7 @@ async def mock_request(method, params, **kwargs): session = await client.create_session( on_permission_request=PermissionHandler.approve_all, enable_citations=True, + enable_file_change_tracking=True, excluded_builtin_agents=["explore"], session_limits={"max_ai_credits": 30}, ) @@ -987,14 +988,17 @@ async def mock_request(method, params, **kwargs): session.session_id, on_permission_request=PermissionHandler.approve_all, enable_citations=False, + enable_file_change_tracking=False, excluded_builtin_agents=["task"], session_limits={"max_ai_credits": 15}, ) assert captured["session.create"]["enableCitations"] is True + assert captured["session.create"]["enableFileChangeTracking"] is True assert captured["session.create"]["excludedBuiltinAgents"] == ["explore"] assert captured["session.create"]["sessionLimits"] == {"maxAiCredits": 30} assert captured["session.resume"]["enableCitations"] is False + assert captured["session.resume"]["enableFileChangeTracking"] is False assert captured["session.resume"]["excludedBuiltinAgents"] == ["task"] assert captured["session.resume"]["sessionLimits"] == {"maxAiCredits": 15} finally: diff --git a/rust/src/types.rs b/rust/src/types.rs index d3c4faa16..1946ded08 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -2093,6 +2093,9 @@ pub struct SessionConfig { pub enable_session_telemetry: Option, /// **Experimental.** Enables native model citations for supported providers. pub enable_citations: Option, + /// Opts in to capturing file changes from the first turn for session rewind + /// and cumulative session diff. + pub enable_file_change_tracking: Option, /// **Experimental.** Limits applied to this session's current accounting window. pub session_limits: Option, /// Per-property overrides for model capabilities, deep-merged over @@ -2283,6 +2286,10 @@ impl std::fmt::Debug for SessionConfig { .field("capi", &self.capi) .field("enable_session_telemetry", &self.enable_session_telemetry) .field("enable_citations", &self.enable_citations) + .field( + "enable_file_change_tracking", + &self.enable_file_change_tracking, + ) .field("session_limits", &self.session_limits) .field("model_capabilities", &self.model_capabilities) .field("memory", &self.memory) @@ -2402,6 +2409,7 @@ impl Default for SessionConfig { models: None, enable_session_telemetry: None, enable_citations: None, + enable_file_change_tracking: None, session_limits: None, model_capabilities: None, memory: None, @@ -2566,6 +2574,7 @@ impl SessionConfig { models: self.models, enable_session_telemetry: self.enable_session_telemetry, enable_citations: self.enable_citations, + enable_file_change_tracking: self.enable_file_change_tracking, session_limits: self.session_limits, model_capabilities: self.model_capabilities, memory: self.memory, @@ -3078,6 +3087,13 @@ impl SessionConfig { self } + /// Opt in to capturing file changes from the first turn for session rewind + /// and cumulative session diff. + pub fn with_enable_file_change_tracking(mut self, enable: bool) -> Self { + self.enable_file_change_tracking = Some(enable); + self + } + /// **Experimental.** Set limits for this session's current accounting window. pub fn with_session_limits(mut self, limits: SessionLimitsConfig) -> Self { self.session_limits = Some(limits); @@ -3369,6 +3385,10 @@ pub struct ResumeSessionConfig { pub enable_session_telemetry: Option, /// **Experimental.** Enables native model citations for supported providers. pub enable_citations: Option, + /// Opts in to capturing file changes for session rewind and cumulative + /// session diff when the resumed session has a valid baseline. Earlier + /// untracked changes cannot be reconstructed. + pub enable_file_change_tracking: Option, /// **Experimental.** Limits applied to this session's current accounting window. pub session_limits: Option, /// Per-property model capability overrides on resume. @@ -3532,6 +3552,10 @@ impl std::fmt::Debug for ResumeSessionConfig { .field("capi", &self.capi) .field("enable_session_telemetry", &self.enable_session_telemetry) .field("enable_citations", &self.enable_citations) + .field( + "enable_file_change_tracking", + &self.enable_file_change_tracking, + ) .field("session_limits", &self.session_limits) .field("model_capabilities", &self.model_capabilities) .field("memory", &self.memory) @@ -3695,6 +3719,7 @@ impl ResumeSessionConfig { models: self.models, enable_session_telemetry: self.enable_session_telemetry, enable_citations: self.enable_citations, + enable_file_change_tracking: self.enable_file_change_tracking, session_limits: self.session_limits, model_capabilities: self.model_capabilities, memory: self.memory, @@ -3791,6 +3816,7 @@ impl ResumeSessionConfig { models: None, enable_session_telemetry: None, enable_citations: None, + enable_file_change_tracking: None, session_limits: None, model_capabilities: None, memory: None, @@ -4280,6 +4306,13 @@ impl ResumeSessionConfig { self } + /// Opt in to capturing file changes for session rewind and cumulative + /// session diff when the resumed session has a valid baseline. + pub fn with_enable_file_change_tracking(mut self, enable: bool) -> Self { + self.enable_file_change_tracking = Some(enable); + self + } + /// **Experimental.** Set limits for this session's current accounting window. pub fn with_session_limits(mut self, limits: SessionLimitsConfig) -> Self { self.session_limits = Some(limits); diff --git a/rust/src/wire.rs b/rust/src/wire.rs index 53ea1c448..21b61a7f9 100644 --- a/rust/src/wire.rs +++ b/rust/src/wire.rs @@ -154,6 +154,8 @@ pub(crate) struct SessionCreateWire { #[serde(skip_serializing_if = "Option::is_none")] pub enable_citations: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub enable_file_change_tracking: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub session_limits: Option, #[serde(skip_serializing_if = "Option::is_none")] pub model_capabilities: Option, @@ -302,6 +304,8 @@ pub(crate) struct SessionResumeWire { #[serde(skip_serializing_if = "Option::is_none")] pub enable_citations: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub enable_file_change_tracking: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub session_limits: Option, #[serde(skip_serializing_if = "Option::is_none")] pub model_capabilities: Option, diff --git a/rust/tests/e2e.rs b/rust/tests/e2e.rs index 3a698abd1..03723dfb1 100644 --- a/rust/tests/e2e.rs +++ b/rust/tests/e2e.rs @@ -66,6 +66,8 @@ mod permissions; mod pre_mcp_tool_call_hook; #[path = "e2e/provider_endpoint.rs"] mod provider_endpoint; +#[path = "e2e/rewind.rs"] +mod rewind; #[path = "e2e/rpc_additional_edge_cases.rs"] mod rpc_additional_edge_cases; #[path = "e2e/rpc_agent.rs"] diff --git a/rust/tests/e2e/rewind.rs b/rust/tests/e2e/rewind.rs new file mode 100644 index 000000000..990c45091 --- /dev/null +++ b/rust/tests/e2e/rewind.rs @@ -0,0 +1,131 @@ +use std::path::Path; +use std::time::Duration; + +use github_copilot_sdk::rpc::{ + HistoryListRewindPointsResult, HistoryPreviewRewindRequest, HistoryRewindMode, + HistoryRewindOutcome, HistoryRewindRequest, +}; + +use super::support::assistant_message_content; + +const FILE_NAME: &str = "rewind-sdk.txt"; +const FILE_CONTENT: &str = "SDK rewind content"; + +#[tokio::test] +async fn should_restore_tracked_file_and_conversation() { + super::support::with_shared_e2e_context( + &E2E, + "rewind", + "should_restore_tracked_file_and_conversation", + |ctx| { + Box::pin(async move { + ctx.set_default_copilot_user(); + let file_path = ctx.work_dir().join(FILE_NAME); + let client = ctx.start_client().await; + let session = client + .create_session( + ctx.approve_all_session_config() + .with_model("claude-sonnet-4.5") + .with_enable_file_change_tracking(true), + ) + .await + .expect("create session"); + + let response = session + .send_and_wait(format!( + "Use the create tool to create {FILE_NAME} containing exactly \ + {FILE_CONTENT}. After the tool succeeds, reply with exactly \ + SDK_REWIND_DONE." + )) + .await + .expect("send rewind setup prompt") + .expect("assistant message"); + assert_eq!(assistant_message_content(&response), "SDK_REWIND_DONE"); + assert_eq!( + std::fs::read_to_string(&file_path).expect("read tracked file"), + FILE_CONTENT + ); + + let rewind_points = wait_for_rewind_points(&session).await; + assert!(rewind_points.file_change_tracking_enabled); + assert_eq!(rewind_points.points.len(), 1); + let rewind_point = &rewind_points.points[0]; + assert!(rewind_point.can_restore_files); + assert_eq!(rewind_point.file_count, 1); + + let preview = session + .rpc() + .history() + .preview_rewind(HistoryPreviewRewindRequest { + event_id: rewind_point.event_id.clone(), + }) + .await + .expect("preview rewind"); + assert!(preview.available); + assert_eq!(preview.files.len(), 1); + assert_same_path(&file_path, Path::new(&preview.files[0].path)); + + let rewind = session + .rpc() + .history() + .rewind(HistoryRewindRequest { + event_id: rewind_point.event_id.clone(), + mode: HistoryRewindMode::ConversationAndFiles, + }) + .await + .expect("rewind conversation and files"); + assert_eq!(rewind.outcome, HistoryRewindOutcome::Success); + assert!(rewind.events_removed.is_some_and(|count| count > 0)); + assert_eq!(rewind.restored_files.len(), 1); + assert_same_path(&file_path, Path::new(&rewind.restored_files[0])); + assert!(!file_path.exists()); + + let events = session.get_events().await.expect("get events after rewind"); + assert!(events.iter().all(|event| event.id != rewind_point.event_id)); + + session.disconnect().await.expect("disconnect session"); + client.stop().await.expect("stop client"); + }) + }, + ) + .await; +} + +async fn wait_for_rewind_points( + session: &github_copilot_sdk::session::Session, +) -> HistoryListRewindPointsResult { + let deadline = tokio::time::Instant::now() + Duration::from_secs(10); + loop { + let result = session + .rpc() + .history() + .list_rewind_points() + .await + .expect("list rewind points"); + if result.unavailable_reason.is_none() { + return result; + } + assert!( + tokio::time::Instant::now() < deadline, + "timed out waiting for rewind points" + ); + tokio::time::sleep(Duration::from_millis(100)).await; + } +} + +fn assert_same_path(expected: &Path, actual: &Path) { + let expected = expected.to_string_lossy(); + let actual = actual.to_string_lossy(); + if cfg!(windows) { + let expected = expected.replace('\\', "/"); + let actual = actual.replace('\\', "/"); + assert!( + expected.eq_ignore_ascii_case(&actual), + "expected path {expected:?}, got {actual:?}" + ); + } else { + assert_eq!(expected, actual); + } +} + +static E2E: super::support::SharedE2eGroup = super::support::SharedE2eGroup::standard("rewind", 1); diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 727911081..83a8489f2 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -641,6 +641,7 @@ async fn create_session_sends_new_session_options() { SessionConfig::default() .with_excluded_builtin_agents(["explore"]) .with_enable_citations(true) + .with_enable_file_change_tracking(true) .with_session_limits(SessionLimitsConfig { max_ai_credits: Some(30.0), }), @@ -657,6 +658,7 @@ async fn create_session_sends_new_session_options() { serde_json::json!(["explore"]) ); assert_eq!(request["params"]["enableCitations"], true); + assert_eq!(request["params"]["enableFileChangeTracking"], true); assert_eq!(request["params"]["sessionLimits"]["maxAiCredits"], 30.0); let id = request["id"].as_u64().unwrap(); @@ -685,6 +687,7 @@ async fn resume_session_sends_new_session_options() { ResumeSessionConfig::new(SessionId::from("session-options")) .with_excluded_builtin_agents(["task"]) .with_enable_citations(false) + .with_enable_file_change_tracking(false) .with_session_limits(SessionLimitsConfig { max_ai_credits: Some(15.0), }), @@ -702,6 +705,7 @@ async fn resume_session_sends_new_session_options() { serde_json::json!(["task"]) ); assert_eq!(request["params"]["enableCitations"], false); + assert_eq!(request["params"]["enableFileChangeTracking"], false); assert_eq!(request["params"]["sessionLimits"]["maxAiCredits"], 15.0); server_respond_create(&mut server_write, &request, "session-options").await; diff --git a/test/snapshots/rewind/should_restore_tracked_file_and_conversation.yaml b/test/snapshots/rewind/should_restore_tracked_file_and_conversation.yaml new file mode 100644 index 000000000..2ef3733e0 --- /dev/null +++ b/test/snapshots/rewind/should_restore_tracked_file_and_conversation.yaml @@ -0,0 +1,21 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Use the create tool to create rewind-sdk.txt containing exactly SDK rewind content. After the tool succeeds, + reply with exactly SDK_REWIND_DONE. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: create + arguments: '{"path":"${workdir}/rewind-sdk.txt","file_text":"SDK rewind content"}' + - role: tool + tool_call_id: toolcall_0 + content: Created file ${workdir}/rewind-sdk.txt with 18 characters + - role: assistant + content: SDK_REWIND_DONE From d9a6fabb03a98900afbba7fa39e31c9ee6179cd1 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Wed, 12 Aug 2026 12:57:27 -0700 Subject: [PATCH 55/72] [java] Add linux-x64 implementation of in process Copilot CLI (#2301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(java): embed Rust CLI runtime as native module Squashed from PR #2295 (branch edburns/…-review-02). Includes Java multi-module Maven restructure, copilot-native submodule for bundling the Rust CLI runtime, codegen updates, and related workflow changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 90cbda40-cda3-4ecd-b381-9f9ba0573d0a * java: add flatten-maven-plugin to produce self-contained deployed POMs Child modules (copilot-sdk-java, copilot-sdk-java-runtime) inherit from copilot-sdk-java-parent, which is not published to Maven Central. Without flattening, consumers resolving a child artifact would fail to resolve the parent POM. The flatten-maven-plugin (ossrh mode) inlines all inherited metadata so the deployed POM is fully self-contained. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * java: only release callback GC root after successful connection_close Previously the tracked callback was removed in a finally block, releasing its GC root even when native connection_close failed or threw. Native code could still retain and invoke the stale function pointer, crashing the JVM after JNA collected the callback. Now the callback reference is only removed from trackedCallbacks when the native call succeeds, ensuring the function pointer stays rooted while native code may still hold it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * java: remove spike test library references, use real native runtime Remove CallbackTestLib interface and all tests that depended on the spike libcallback_test.so from the removed 1917-java-embed-rust-cli-runtime-remove-before-merge directory. Rewrite the 3 duplicate-load guard tests to use NativeRuntimeLoader.resolve() to locate the real runtime.node binary instead of the spike library. Remove the startWithSpikeLibrarySupportsLifecycleAndDataFlow integration test from FfiRuntimeHostTest — this functionality is covered by E2E tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * docs: add JNA dependency to in-process mode README snippet JNA is declared optional in the SDK POM so subprocess-mode users don't pull it transitively. The in-process mode section was missing this required third dependency, which would cause NoClassDefFoundError at runtime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * java: add empty javadoc and sources JARs for copilot-native module Maven Central requires javadoc and sources classifier JARs for every non-POM artifact. Since copilot-native has no Java sources, the parent's maven-javadoc-plugin produced nothing. Add explicit empty-archive executions to maven-jar-plugin so the module passes Central validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * java: use size_t-width type for native length parameters The C ABI declares all buffer-length parameters as size_t (8 bytes on 64-bit), but the Java FFI layer was using int (always 4 bytes). While harmless for current JSON-RPC payloads, this is incorrect on 64-bit platforms and would be wrong on Windows x64 where NativeLong (C long) is also only 4 bytes. Introduce SizeT, a minimal IntegerType subclass sized via Native.SIZE_T_SIZE, and use it in CopilotRuntimeLibrary and OutboundCallback. The NativeBinding Java abstraction layer keeps int parameters; JnaNativeBinding converts at the boundary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * java: remove global JNA callback exception handler The process-wide Native.setCallbackExceptionHandler was redundant: the local catch (Throwable) in createOutboundCallback() already prevents any exception from escaping across the FFI boundary. The global mutation affected unrelated JNA callbacks in the same process and was never restored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * ci: make java-sdk-inprocess test job blocking Remove continue-on-error so FFI regressions in the in-process transport block PRs. The in-process transport is now production code and must not silently regress. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * fix: update jbang-example.java path after move to sdk/ submodule The JBang example moved from java/jbang-example.java to java/sdk/jbang-example.java but two references were not updated: the runnable JBang URL in README.md and the release workflow's update-documentation-versions script invocation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * docs: fix ADR relative links in README The ADRs are at java/docs/adr/, not java/sdk/docs/adr/. Fix the relative links for ADR-006 and ADR-004. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c * Regenerate Java codegen output Auto-committed by java-codegen-check workflow. * fix(java): retain JNA callbacks for process lifetime Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de * fix(java): publish executable CLI atomically Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de * docs(java): align native runtime ADR Document strict in-process selection, the implemented runtime resolution order, current artifact contents, and the intentionally narrow PATH compatibility fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de * fix(java): key native cache by runtime version Read the packaged native version before cache lookup so mutable SDK snapshot builds cannot reuse stale runtime and CLI artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de * fix(java): validate staged native artifacts Include the bundled CLI digest in the incremental staging stamp, require complete platform metadata, and verify the classifier JAR contains the CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de * fix(java): complete Maven module migration Restore pre-migration dependency and plugin versions, publish correct child project metadata, and update repository consumers to the moved SDK paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Copilot-Session: 90cbda40-cda3-4ecd-b381-9f9ba0573d0a Copilot-Session: 3a69f33e-eea6-4b8c-9ef0-8b4d56d53a9c Copilot-Session: 108d08d4-0724-4414-bed8-2771b12b58de --- .githooks/pre-commit | 6 +- .github/actions/java-test-report/action.yml | 6 +- .github/copilot-instructions.md | 14 +- .../new-java-e2e-test-yaml-and-test/SKILL.md | 8 +- .github/workflows/codegen-check.yml | 4 +- .github/workflows/docs-validation.yml | 3 +- ...en-code-to-accept-upgrade-changes.lock.yml | 2 +- ...dwritten-code-to-accept-upgrade-changes.md | 12 +- .github/workflows/java-codegen-check.yml | 4 +- .github/workflows/java-codegen-fix.lock.yml | 2 +- .github/workflows/java-codegen-fix.md | 20 +- .github/workflows/java-publish-maven.yml | 4 +- .github/workflows/java-sdk-tests.yml | 59 +- .github/workflows/java-smoke-test.yml | 4 +- .../workflows/sdk-consistency-review.lock.yml | 2 +- .github/workflows/sdk-consistency-review.md | 2 +- .../workflows/update-copilot-dependency.yml | 4 +- .gitignore | 3 +- java/README.md | 52 +- java/copilot-native/pom.xml | 269 ++++++ java/copilot-native/scripts/fetch-native.mjs | 134 +++ .../scripts/fetch-native.test.mjs | 124 +++ java/copilot-native/src/main/java/.gitkeep | 0 java/copilot-native/src/main/javadoc/.gitkeep | 0 .../native/lib/copilot-runtime.properties | 12 + .../adr/adr-007-native-bundling-strategy.md | 333 ++++---- java/pom.xml | 802 +++--------------- java/scripts/codegen/java.ts | 14 +- .../config/checkstyle/checkstyle.xml | 0 .../config/spotbugs/spotbugs-exclude.xml | 0 java/{ => sdk}/jbang-example.java | 0 java/sdk/pom.xml | 798 +++++++++++++++++ .../github/copilot/generated/AbortEvent.java | 0 .../github/copilot/generated/AbortReason.java | 0 .../copilot/generated/AssistantIdleEvent.java | 0 .../generated/AssistantIntentEvent.java | 0 .../generated/AssistantMessageDeltaEvent.java | 0 .../generated/AssistantMessageEvent.java | 0 .../AssistantMessageServerTools.java | 0 .../generated/AssistantMessageStartEvent.java | 0 .../AssistantMessageToolRequest.java | 0 .../AssistantMessageToolRequestType.java | 0 .../AssistantReasoningDeltaEvent.java | 0 .../generated/AssistantReasoningEvent.java | 0 .../AssistantServerToolProgressEvent.java | 0 .../AssistantStreamingDeltaEvent.java | 0 .../AssistantToolCallDeltaEvent.java | 0 .../generated/AssistantTurnEndEvent.java | 0 .../generated/AssistantTurnRetryEvent.java | 0 .../generated/AssistantTurnStartEvent.java | 0 .../generated/AssistantUsageApiEndpoint.java | 0 .../generated/AssistantUsageCopilotUsage.java | 0 ...AssistantUsageCopilotUsageTokenDetail.java | 0 .../generated/AssistantUsageEvent.java | 0 .../AssistantUsageQuotaSnapshot.java | 0 .../AutoModeResolvedReasoningBucket.java | 0 .../AutoModeSwitchCompletedEvent.java | 0 .../AutoModeSwitchRequestedEvent.java | 0 .../generated/AutoModeSwitchResponse.java | 0 .../AutopilotObjectiveChangedOperation.java | 0 .../AutopilotObjectiveChangedStatus.java | 0 .../copilot/generated/BinaryAssetType.java | 0 .../CanvasRegistryChangedCanvas.java | 0 .../CanvasRegistryChangedCanvasAction.java | 0 .../generated/CapabilitiesChangedEvent.java | 0 .../generated/CapabilitiesChangedUI.java | 0 .../copilot/generated/CitableSource.java | 0 .../copilot/generated/CitationProvider.java | 0 .../copilot/generated/CitationReference.java | 0 .../copilot/generated/CitationSource.java | 0 .../copilot/generated/CitationSpan.java | 0 .../github/copilot/generated/Citations.java | 0 .../generated/CommandCompletedEvent.java | 0 .../generated/CommandExecuteEvent.java | 0 .../copilot/generated/CommandQueuedEvent.java | 0 .../generated/CommandsChangedCommand.java | 0 .../generated/CommandsChangedEvent.java | 0 ...ompactionCompleteCompactionTokensUsed.java | 0 ...pleteCompactionTokensUsedCopilotUsage.java | 0 ...tionTokensUsedCopilotUsageTokenDetail.java | 0 .../copilot/generated/CompactionTrigger.java | 0 .../github/copilot/generated/ContextTier.java | 0 .../generated/CustomAgentsUpdatedAgent.java | 0 .../generated/ElicitationCompletedAction.java | 0 .../generated/ElicitationCompletedEvent.java | 0 .../generated/ElicitationRequestedEvent.java | 0 .../generated/ElicitationRequestedMode.java | 0 .../generated/ElicitationRequestedSchema.java | 0 .../copilot/generated/ExitPlanModeAction.java | 0 .../generated/ExitPlanModeCompletedEvent.java | 0 .../generated/ExitPlanModeRequestedEvent.java | 0 .../generated/ExtensionsLoadedExtension.java | 0 .../ExtensionsLoadedExtensionSource.java | 0 .../ExtensionsLoadedExtensionStatus.java | 0 .../generated/ExternalToolCompletedEvent.java | 0 .../generated/ExternalToolRequestedEvent.java | 0 .../generated/FactoryRunUpdatedEvent.java | 0 .../generated/GitHubMcpToolConfig.java | 0 .../copilot/generated/HandoffRepository.java | 0 .../copilot/generated/HandoffSourceType.java | 0 .../github/copilot/generated/HeaderEntry.java | 0 .../copilot/generated/HookEndError.java | 0 .../copilot/generated/HookEndEvent.java | 0 .../copilot/generated/HookProgressEvent.java | 0 .../copilot/generated/HookStartEvent.java | 0 .../ManagedSettingsEnforcedAction.java | 0 .../ManagedSettingsEnforcedEscalation.java | 0 .../ManagedSettingsResolvedSource.java | 0 .../McpAppToolCallCompleteError.java | 0 .../McpAppToolCallCompleteEvent.java | 0 .../McpAppToolCallCompleteToolMeta.java | 0 .../McpAppToolCallCompleteToolMetaUI.java | 0 .../McpHeadersRefreshCompletedEvent.java | 0 .../McpHeadersRefreshCompletedOutcome.java | 0 .../McpHeadersRefreshRequiredEvent.java | 0 .../McpHeadersRefreshRequiredReason.java | 0 .../generated/McpOauthCompletedEvent.java | 0 .../generated/McpOauthCompletionOutcome.java | 0 .../generated/McpOauthHttpResponse.java | 0 .../generated/McpOauthRequestReason.java | 0 .../generated/McpOauthRequiredEvent.java | 0 .../McpOauthRequiredStaticClientConfig.java | 0 .../McpOauthWWWAuthenticateParams.java | 0 .../generated/McpPromptsListChangedEvent.java | 0 .../McpResourcesListChangedEvent.java | 0 .../copilot/generated/McpServerSource.java | 0 .../copilot/generated/McpServerStatus.java | 0 .../copilot/generated/McpServerTransport.java | 0 .../generated/McpServersLoadedServer.java | 0 .../generated/McpToolsListChangedEvent.java | 0 .../ModelCallFailureBadRequestKind.java | 0 .../generated/ModelCallFailureEvent.java | 0 .../generated/ModelCallFailureKind.java | 0 .../ModelCallFailureRequestFingerprint.java | 0 .../generated/ModelCallFailureSource.java | 0 .../generated/ModelCallFailureTransport.java | 0 .../generated/ModelCallStartEvent.java | 0 .../PendingMessagesModifiedEvent.java | 0 .../generated/PermissionAllowAllMode.java | 0 .../generated/PermissionCompletedEvent.java | 0 .../generated/PermissionRequestedEvent.java | 0 .../generated/PlanChangedOperation.java | 0 .../copilot/generated/ReasoningSummary.java | 0 .../generated/SamplingCompletedEvent.java | 0 .../generated/SamplingRequestedEvent.java | 0 .../copilot/generated/ScheduleOrigin.java | 0 .../SessionAutoModeResolvedEvent.java | 0 ...SessionAutopilotObjectiveChangedEvent.java | 0 .../SessionBackgroundTasksChangedEvent.java | 0 .../generated/SessionBinaryAssetEvent.java | 0 .../generated/SessionCanvasClosedEvent.java | 0 .../generated/SessionCanvasOpenedEvent.java | 0 .../generated/SessionCanvasRecordedEvent.java | 0 .../SessionCanvasRegistryChangedEvent.java | 0 .../generated/SessionCanvasRemovedEvent.java | 0 .../SessionCanvasUnavailableEvent.java | 0 .../SessionCompactionCompleteEvent.java | 0 .../SessionCompactionStartEvent.java | 0 .../generated/SessionContextChangedEvent.java | 0 .../generated/SessionContextClearedEvent.java | 0 .../SessionCustomAgentsUpdatedEvent.java | 0 .../SessionCustomNotificationEvent.java | 0 .../copilot/generated/SessionErrorEvent.java | 0 .../copilot/generated/SessionEvent.java | 0 ...ssionExtensionsAttachmentsPushedEvent.java | 0 .../SessionExtensionsLoadedEvent.java | 0 .../generated/SessionHandoffEvent.java | 0 .../copilot/generated/SessionIdleEvent.java | 0 .../copilot/generated/SessionInfoEvent.java | 0 .../generated/SessionLimitsConfig.java | 0 .../SessionLimitsExhaustedCompletedEvent.java | 0 .../SessionLimitsExhaustedRequestedEvent.java | 0 .../SessionLimitsExhaustedResponse.java | 0 .../SessionLimitsExhaustedResponseAction.java | 0 .../SessionManagedSettingsEnforcedEvent.java | 0 .../SessionManagedSettingsResolvedEvent.java | 0 .../SessionMcpServerStatusChangedEvent.java | 0 .../SessionMcpServersLoadedEvent.java | 0 .../github/copilot/generated/SessionMode.java | 0 .../generated/SessionModeChangedEvent.java | 0 .../generated/SessionModelChangeEvent.java | 0 .../SessionPermissionsChangedEvent.java | 0 .../generated/SessionPlanChangedEvent.java | 0 .../SessionRemoteSteerableChangedEvent.java | 0 .../copilot/generated/SessionResumeEvent.java | 0 .../SessionScheduleCancelledEvent.java | 0 .../SessionScheduleCreatedEvent.java | 0 .../SessionScheduleRearmedEvent.java | 0 .../SessionSessionLimitsChangedEvent.java | 0 .../generated/SessionShutdownEvent.java | 0 .../generated/SessionSkillsLoadedEvent.java | 0 .../generated/SessionSnapshotRewindEvent.java | 0 .../copilot/generated/SessionStartEvent.java | 0 .../generated/SessionTaskCompleteEvent.java | 0 .../generated/SessionTitleChangedEvent.java | 0 .../generated/SessionTodosChangedEvent.java | 0 .../generated/SessionToolsUpdatedEvent.java | 0 .../generated/SessionTruncationEvent.java | 0 .../SessionUsageCheckpointEvent.java | 0 .../generated/SessionUsageInfoEvent.java | 0 .../generated/SessionWarningEvent.java | 0 .../SessionWorkspaceFileChangedEvent.java | 0 .../generated/ShutdownCodeChanges.java | 0 .../generated/ShutdownModelMetric.java | 0 .../ShutdownModelMetricRequests.java | 0 .../ShutdownModelMetricTokenDetail.java | 0 .../generated/ShutdownModelMetricUsage.java | 0 .../generated/ShutdownTokenDetail.java | 0 .../copilot/generated/ShutdownType.java | 0 .../copilot/generated/SkillInvokedEvent.java | 0 .../generated/SkillInvokedTrigger.java | 0 .../github/copilot/generated/SkillSource.java | 0 .../copilot/generated/SkillsLoadedSkill.java | 0 .../generated/SubagentCompletedEvent.java | 0 .../generated/SubagentDeselectedEvent.java | 0 .../generated/SubagentFailedEvent.java | 0 .../generated/SubagentSelectedEvent.java | 0 .../generated/SubagentStartedEvent.java | 0 .../copilot/generated/SystemMessageEvent.java | 0 .../generated/SystemMessageMetadata.java | 0 .../copilot/generated/SystemMessageRole.java | 0 .../generated/SystemNotificationEvent.java | 0 .../generated/TaskCompletionOutcome.java | 0 .../generated/ToolExecutionCompleteError.java | 0 .../generated/ToolExecutionCompleteEvent.java | 0 .../ToolExecutionCompleteResult.java | 0 .../ToolExecutionCompleteToolDescription.java | 0 ...lExecutionCompleteToolDescriptionMeta.java | 0 ...xecutionCompleteToolDescriptionMetaUI.java | 0 ...mpleteToolDescriptionMetaUIVisibility.java | 0 .../ToolExecutionCompleteUIResource.java | 0 .../ToolExecutionCompleteUIResourceMeta.java | 0 ...ToolExecutionCompleteUIResourceMetaUI.java | 0 ...lExecutionCompleteUIResourceMetaUICsp.java | 0 ...onCompleteUIResourceMetaUIPermissions.java | 0 ...leteUIResourceMetaUIPermissionsCamera.java | 0 ...sourceMetaUIPermissionsClipboardWrite.java | 0 ...IResourceMetaUIPermissionsGeolocation.java | 0 ...UIResourceMetaUIPermissionsMicrophone.java | 0 .../ToolExecutionPartialResultEvent.java | 0 .../generated/ToolExecutionProgressEvent.java | 0 .../generated/ToolExecutionStartEvent.java | 0 .../ToolExecutionStartShellToolInfo.java | 0 .../ToolExecutionStartToolDescription.java | 0 ...ToolExecutionStartToolDescriptionMeta.java | 0 ...olExecutionStartToolDescriptionMetaUI.java | 0 ...nStartToolDescriptionMetaUIVisibility.java | 0 .../generated/ToolSearchActivatedEvent.java | 0 .../generated/ToolUserRequestedEvent.java | 0 .../generated/UnknownSessionEvent.java | 0 .../UsageCheckpointModelCacheState.java | 0 .../generated/UserInputCompletedEvent.java | 0 .../generated/UserInputRequestedEvent.java | 0 .../generated/UserMessageAgentMode.java | 0 .../generated/UserMessageDelivery.java | 0 .../copilot/generated/UserMessageEvent.java | 0 .../github/copilot/generated/Verbosity.java | 0 .../generated/WorkingDirectoryContext.java | 0 .../WorkingDirectoryContextHostType.java | 0 .../WorkspaceFileChangedOperation.java | 0 .../copilot/generated/package-info.java | 0 .../copilot/generated/rpc/AbortReason.java | 0 .../generated/rpc/AccountAllUsers.java | 0 .../rpc/AccountGetCurrentAuthResult.java | 0 .../generated/rpc/AccountGetQuotaParams.java | 0 .../generated/rpc/AccountGetQuotaResult.java | 0 .../generated/rpc/AccountLoginParams.java | 0 .../generated/rpc/AccountLoginResult.java | 0 .../generated/rpc/AccountLogoutParams.java | 0 .../generated/rpc/AccountLogoutResult.java | 0 .../generated/rpc/AccountQuotaSnapshot.java | 0 .../rpc/AdaptiveThinkingSupport.java | 0 .../generated/rpc/AgentDiscoveryPath.java | 0 .../rpc/AgentDiscoveryPathScope.java | 0 .../copilot/generated/rpc/AgentInfo.java | 0 .../generated/rpc/AgentInfoSource.java | 0 .../rpc/AgentRegistryLiveTargetEntry.java | 0 ...tRegistryLiveTargetEntryAttentionKind.java | 0 .../rpc/AgentRegistryLiveTargetEntryKind.java | 0 ...istryLiveTargetEntryLastTerminalEvent.java | 0 .../AgentRegistryLiveTargetEntryStatus.java | 0 .../rpc/AgentRegistryLogCapture.java | 0 ...gentRegistryLogCaptureOpenErrorReason.java | 0 .../rpc/AgentRegistrySpawnError.java | 0 .../rpc/AgentRegistrySpawnParams.java | 0 .../rpc/AgentRegistrySpawnPermissionMode.java | 0 .../AgentRegistrySpawnRegistryTimeout.java | 0 .../rpc/AgentRegistrySpawnResult.java | 0 .../rpc/AgentRegistrySpawnSpawned.java | 0 .../AgentRegistrySpawnValidationError.java | 0 ...gentRegistrySpawnValidationErrorField.java | 0 ...entRegistrySpawnValidationErrorReason.java | 0 .../generated/rpc/AgentsDiscoverParams.java | 0 .../generated/rpc/AgentsDiscoverResult.java | 0 .../rpc/AgentsGetDiscoveryPathsParams.java | 0 .../rpc/AgentsGetDiscoveryPathsResult.java | 0 .../copilot/generated/rpc/AuthInfoType.java | 0 .../rpc/BuiltInModelCatalogEntry.java | 0 .../copilot/generated/rpc/CanvasAction.java | 0 .../rpc/CanvasActionInvokeParams.java | 0 .../generated/rpc/CanvasCloseParams.java | 0 .../generated/rpc/CanvasHostContext.java | 0 .../rpc/CanvasHostContextCapabilities.java | 0 .../generated/rpc/CanvasOpenParams.java | 0 .../generated/rpc/CanvasOpenResult.java | 0 .../generated/rpc/CanvasSessionContext.java | 0 .../generated/rpc/CapiSessionOptions.java | 0 .../generated/rpc/CommandsListResult.java | 0 .../copilot/generated/rpc/ConnectParams.java | 0 .../copilot/generated/rpc/ConnectResult.java | 0 .../rpc/ConnectedRemoteSessionMetadata.java | 0 .../ConnectedRemoteSessionMetadataKind.java | 0 ...nectedRemoteSessionMetadataRepository.java | 0 .../rpc/ContentExclusionPathCheck.java | 0 .../generated/rpc/ContextHeaviestMessage.java | 0 .../copilot/generated/rpc/ContextTier.java | 0 .../generated/rpc/CurrentToolMetadata.java | 0 .../rpc/DebugCollectLogsCollectedEntry.java | 0 .../generated/rpc/DebugCollectLogsEntry.java | 0 .../rpc/DebugCollectLogsEntryKind.java | 0 .../rpc/DebugCollectLogsInclude.java | 0 .../rpc/DebugCollectLogsRedaction.java | 0 .../rpc/DebugCollectLogsResultKind.java | 0 .../rpc/DebugCollectLogsSkippedEntry.java | 0 .../generated/rpc/DebugCollectLogsSource.java | 0 .../rpc/DisableBypassPermissionsMode.java | 0 .../generated/rpc/DiscoveredCanvas.java | 0 .../generated/rpc/DiscoveredExtension.java | 0 .../rpc/DiscoveredExtensionMode.java | 0 .../rpc/DiscoveredExtensionPlugin.java | 0 .../rpc/DiscoveredExtensionSource.java | 0 .../generated/rpc/DiscoveredMcpServer.java | 0 .../rpc/DiscoveredMcpServerType.java | 0 .../generated/rpc/EventsAgentScope.java | 0 .../generated/rpc/EventsCursorStatus.java | 0 .../generated/rpc/EventsReadDirection.java | 0 .../copilot/generated/rpc/Extension.java | 0 .../generated/rpc/ExtensionLaunchProfile.java | 0 ...ExtensionLaunchProviderResolveRequest.java | 0 .../ExtensionLaunchProviderResolveResult.java | 0 .../generated/rpc/ExtensionSource.java | 0 .../generated/rpc/ExtensionStatus.java | 0 .../rpc/ExtensionsDisableParams.java | 0 .../rpc/ExtensionsDiscoverResult.java | 0 .../generated/rpc/ExtensionsEnableParams.java | 0 .../generated/rpc/FactoryAbortParams.java | 0 .../generated/rpc/FactoryAgentOptions.java | 0 .../generated/rpc/FactoryAgentSummary.java | 0 .../generated/rpc/FactoryCurrentPhase.java | 0 .../generated/rpc/FactoryDeclaredLimits.java | 0 .../generated/rpc/FactoryExecuteParams.java | 0 .../generated/rpc/FactoryExecuteResult.java | 0 .../copilot/generated/rpc/FactoryLogLine.java | 0 .../generated/rpc/FactoryLogLineKind.java | 0 .../rpc/FactoryPhaseObservation.java | 0 .../generated/rpc/FactoryPhaseStatus.java | 0 .../generated/rpc/FactoryProgressLine.java | 0 .../generated/rpc/FactoryProgressPage.java | 0 .../generated/rpc/FactoryRunConsumed.java | 0 .../generated/rpc/FactoryRunLimits.java | 0 .../generated/rpc/FactoryRunResult.java | 0 .../generated/rpc/FactoryRunStatus.java | 0 .../generated/rpc/FactoryRunSummary.java | 0 .../generated/rpc/FactoryRunTerminal.java | 0 .../rpc/GitHubTelemetryClientInfo.java | 0 .../generated/rpc/GitHubTelemetryEvent.java | 0 .../rpc/GitHubTelemetryNotification.java | 0 .../rpc/HistoryCompactContextWindow.java | 0 .../rpc/HistoryFileRestoreSkipReason.java | 0 .../rpc/HistoryRewindChangeType.java | 0 .../rpc/HistoryRewindFilePreview.java | 0 .../generated/rpc/HistoryRewindMode.java | 0 .../generated/rpc/HistoryRewindOutcome.java | 0 .../generated/rpc/HistoryRewindPoint.java | 0 .../rpc/HistoryRewindUnavailableReason.java | 0 .../rpc/HistorySkippedFileRestore.java | 0 .../generated/rpc/HookInvokeRequest.java | 0 .../copilot/generated/rpc/HookType.java | 0 .../generated/rpc/HooksInvokeResult.java | 0 .../generated/rpc/InstalledPlugin.java | 0 .../generated/rpc/InstalledPluginInfo.java | 0 .../rpc/InstructionDiscoveryPath.java | 0 .../rpc/InstructionDiscoveryPathKind.java | 0 .../rpc/InstructionDiscoveryPathLocation.java | 0 .../generated/rpc/InstructionSource.java | 0 .../rpc/InstructionSourceLocation.java | 0 .../generated/rpc/InstructionSourceType.java | 0 .../rpc/InstructionsDiscoverParams.java | 0 .../rpc/InstructionsDiscoverResult.java | 0 .../InstructionsGetDiscoveryPathsParams.java | 0 .../InstructionsGetDiscoveryPathsResult.java | 0 .../LlmInferenceHttpRequestChunkRequest.java | 0 .../LlmInferenceHttpRequestChunkResult.java | 0 .../LlmInferenceHttpRequestStartRequest.java | 0 .../LlmInferenceHttpRequestStartResult.java | 0 ...LlmInferenceHttpRequestStartTransport.java | 0 .../LlmInferenceHttpResponseChunkError.java | 0 .../LlmInferenceHttpResponseChunkParams.java | 0 .../LlmInferenceHttpResponseChunkResult.java | 0 .../LlmInferenceHttpResponseStartParams.java | 0 .../LlmInferenceHttpResponseStartResult.java | 0 .../rpc/LlmInferenceSetProviderResult.java | 0 .../rpc/LocalSessionMetadataValue.java | 0 .../rpc/ManagedSettingsReadResult.java | 0 .../generated/rpc/MarketplaceInfo.java | 0 .../generated/rpc/MarketplacePluginInfo.java | 0 .../rpc/MarketplaceRefreshEntry.java | 0 .../generated/rpc/McpAllowedServer.java | 0 .../rpc/McpAppsDiagnoseCapability.java | 0 .../generated/rpc/McpAppsDiagnoseServer.java | 0 .../rpc/McpAppsHostContextDetails.java | 0 ...ostContextDetailsAvailableDisplayMode.java | 0 .../McpAppsHostContextDetailsDisplayMode.java | 0 .../McpAppsHostContextDetailsPlatform.java | 0 .../rpc/McpAppsHostContextDetailsTheme.java | 0 .../generated/rpc/McpAppsResourceContent.java | 0 .../rpc/McpAppsSetHostContextDetails.java | 0 ...ostContextDetailsAvailableDisplayMode.java | 0 ...pAppsSetHostContextDetailsDisplayMode.java | 0 .../McpAppsSetHostContextDetailsPlatform.java | 0 .../McpAppsSetHostContextDetailsTheme.java | 0 .../generated/rpc/McpConfigAddParams.java | 0 .../generated/rpc/McpConfigDisableParams.java | 0 .../generated/rpc/McpConfigEnableParams.java | 0 .../generated/rpc/McpConfigListResult.java | 0 .../generated/rpc/McpConfigRemoveParams.java | 0 .../generated/rpc/McpConfigUpdateParams.java | 0 .../generated/rpc/McpDiscoverParams.java | 0 .../generated/rpc/McpDiscoverResult.java | 0 .../rpc/McpExecuteSamplingRequest.java | 0 .../rpc/McpExecuteSamplingResult.java | 0 .../generated/rpc/McpFilteredServer.java | 0 .../copilot/generated/rpc/McpHostState.java | 0 .../generated/rpc/McpOauthLoginGrantType.java | 0 .../copilot/generated/rpc/McpResource.java | 0 .../generated/rpc/McpResourceAnnotations.java | 0 .../generated/rpc/McpResourceContent.java | 0 .../generated/rpc/McpResourceIcon.java | 0 .../generated/rpc/McpResourceTemplate.java | 0 .../rpc/McpSamplingExecutionAction.java | 0 .../copilot/generated/rpc/McpServer.java | 0 .../generated/rpc/McpServerFailureInfo.java | 0 .../generated/rpc/McpServerNeedsAuthInfo.java | 0 .../generated/rpc/McpServerSource.java | 0 .../generated/rpc/McpServerStatus.java | 0 .../rpc/McpSetEnvValueModeDetails.java | 0 .../copilot/generated/rpc/McpToolUi.java | 0 .../generated/rpc/McpToolUiVisibility.java | 0 .../copilot/generated/rpc/McpTools.java | 0 .../generated/rpc/MemoryConfiguration.java | 0 .../rpc/MetadataSnapshotCurrentMode.java | 0 .../rpc/MetadataSnapshotRemoteMetadata.java | 0 ...adataSnapshotRemoteMetadataRepository.java | 0 ...etadataSnapshotRemoteMetadataTaskType.java | 0 .../github/copilot/generated/rpc/Model.java | 0 .../copilot/generated/rpc/ModelBilling.java | 0 .../generated/rpc/ModelBillingPromo.java | 0 .../rpc/ModelBillingTokenPrices.java | 0 .../ModelBillingTokenPricesLongContext.java | 0 .../generated/rpc/ModelCapabilities.java | 0 .../rpc/ModelCapabilitiesLimits.java | 0 .../rpc/ModelCapabilitiesLimitsVision.java | 0 .../rpc/ModelCapabilitiesOverride.java | 0 .../rpc/ModelCapabilitiesOverrideLimits.java | 0 ...ModelCapabilitiesOverrideLimitsVision.java | 0 .../ModelCapabilitiesOverrideSupports.java | 0 .../rpc/ModelCapabilitiesSupports.java | 0 .../generated/rpc/ModelPickerCategory.java | 0 .../rpc/ModelPickerPriceCategory.java | 0 .../copilot/generated/rpc/ModelPolicy.java | 0 .../generated/rpc/ModelPolicyState.java | 0 .../rpc/ModelsGetBuiltInCatalogResult.java | 0 .../generated/rpc/ModelsListParams.java | 0 .../generated/rpc/ModelsListResult.java | 0 .../generated/rpc/NamedProviderConfig.java | 0 .../generated/rpc/OpenCanvasInstance.java | 0 ...pdateAdditionalContentExclusionPolicy.java | 0 ...eAdditionalContentExclusionPolicyRule.java | 0 ...ionalContentExclusionPolicyRuleSource.java | 0 ...AdditionalContentExclusionPolicyScope.java | 0 .../rpc/OptionsUpdateContextTier.java | 0 .../rpc/OptionsUpdateEnvValueMode.java | 0 .../rpc/OptionsUpdateReasoningSummary.java | 0 .../OptionsUpdateToolFilterPrecedence.java | 0 .../rpc/PendingPermissionRequest.java | 0 .../rpc/PermissionDecisionContext.java | 0 .../rpc/PermissionDecisionOutcome.java | 0 .../rpc/PermissionDecisionSource.java | 0 .../rpc/PermissionDecisionSurface.java | 0 .../generated/rpc/PermissionLocationType.java | 0 .../generated/rpc/PermissionPathsConfig.java | 0 .../copilot/generated/rpc/PermissionRule.java | 0 .../generated/rpc/PermissionRulesSet.java | 0 .../generated/rpc/PermissionUrlsConfig.java | 0 .../rpc/PermissionsAllowAllMode.java | 0 ...igureAdditionalContentExclusionPolicy.java | 0 ...eAdditionalContentExclusionPolicyRule.java | 0 ...ionalContentExclusionPolicyRuleSource.java | 0 ...AdditionalContentExclusionPolicyScope.java | 0 .../rpc/PermissionsModifyRulesScope.java | 0 .../rpc/PermissionsSetAllowAllSource.java | 0 .../rpc/PermissionsSetApproveAllSource.java | 0 .../copilot/generated/rpc/PingParams.java | 0 .../copilot/generated/rpc/PingResult.java | 0 .../generated/rpc/PlanSqlTodoDependency.java | 0 .../generated/rpc/PlanSqlTodosRow.java | 0 .../github/copilot/generated/rpc/Plugin.java | 0 .../generated/rpc/PluginUpdateAllEntry.java | 0 .../generated/rpc/PluginsDisableParams.java | 0 .../generated/rpc/PluginsEnableParams.java | 0 .../generated/rpc/PluginsInstallParams.java | 0 .../generated/rpc/PluginsInstallResult.java | 0 .../generated/rpc/PluginsListResult.java | 0 .../rpc/PluginsMarketplacesAddParams.java | 0 .../rpc/PluginsMarketplacesAddResult.java | 0 .../rpc/PluginsMarketplacesBrowseParams.java | 0 .../rpc/PluginsMarketplacesBrowseResult.java | 0 .../rpc/PluginsMarketplacesListResult.java | 0 .../rpc/PluginsMarketplacesRefreshParams.java | 0 .../rpc/PluginsMarketplacesRefreshResult.java | 0 .../rpc/PluginsMarketplacesRemoveParams.java | 0 .../rpc/PluginsMarketplacesRemoveResult.java | 0 .../generated/rpc/PluginsUninstallParams.java | 0 .../generated/rpc/PluginsUpdateAllResult.java | 0 .../generated/rpc/PluginsUpdateParams.java | 0 .../generated/rpc/PluginsUpdateResult.java | 0 .../copilot/generated/rpc/ProviderConfig.java | 0 .../generated/rpc/ProviderConfigAzure.java | 0 .../rpc/ProviderConfigTransport.java | 0 .../generated/rpc/ProviderConfigType.java | 0 .../generated/rpc/ProviderConfigWireApi.java | 0 .../rpc/ProviderEndpointTransport.java | 0 .../generated/rpc/ProviderEndpointType.java | 0 .../rpc/ProviderEndpointWireApi.java | 0 .../generated/rpc/ProviderModelConfig.java | 0 .../generated/rpc/ProviderSessionToken.java | 0 .../rpc/ProviderTokenGetTokenParams.java | 0 .../rpc/ProviderTokenGetTokenResult.java | 0 .../generated/rpc/QueueInsertMessage.java | 0 .../generated/rpc/QueuePendingItems.java | 0 .../generated/rpc/QueuePendingItemsKind.java | 0 .../generated/rpc/ReasoningSummary.java | 0 .../generated/rpc/RemoteControlConfig.java | 0 .../RemoteControlConfigExistingMcSession.java | 0 .../rpc/RemoteSessionMetadataRepository.java | 0 .../rpc/RemoteSessionMetadataTaskType.java | 0 .../rpc/RemoteSessionMetadataValue.java | 0 .../generated/rpc/RemoteSessionMode.java | 0 .../rpc/RemoteSessionRepository.java | 0 .../copilot/generated/rpc/RpcCaller.java | 0 .../copilot/generated/rpc/RpcMapper.java | 0 .../copilot/generated/rpc/RunOptions.java | 0 .../copilot/generated/rpc/SandboxConfig.java | 0 .../generated/rpc/SandboxConfigAuth.java | 0 .../rpc/SandboxConfigUserPolicy.java | 0 .../SandboxConfigUserPolicyExperimental.java | 0 ...xConfigUserPolicyExperimentalSeatbelt.java | 0 .../SandboxConfigUserPolicyFilesystem.java | 0 .../rpc/SandboxConfigUserPolicyNetwork.java | 0 .../SandboxConfigUserPolicyNetworkProxy.java | 0 .../rpc/SandboxConfigUserPolicySeatbelt.java | 0 .../copilot/generated/rpc/ScheduleEntry.java | 0 .../rpc/SecretsAddFilterValuesParams.java | 0 .../rpc/SecretsAddFilterValuesResult.java | 0 .../copilot/generated/rpc/SendAgentMode.java | 0 .../generated/rpc/SendMessageItem.java | 0 .../copilot/generated/rpc/SendMode.java | 0 .../generated/rpc/ServerAccountApi.java | 0 .../generated/rpc/ServerAgentRegistryApi.java | 0 .../generated/rpc/ServerAgentsApi.java | 0 .../generated/rpc/ServerCommandsApi.java | 0 .../generated/rpc/ServerExtensionsApi.java | 0 .../generated/rpc/ServerInstructionsApi.java | 0 .../generated/rpc/ServerLlmInferenceApi.java | 0 .../rpc/ServerManagedSettingsApi.java | 0 .../copilot/generated/rpc/ServerMcpApi.java | 0 .../generated/rpc/ServerMcpConfigApi.java | 0 .../generated/rpc/ServerModelsApi.java | 0 .../generated/rpc/ServerPluginsApi.java | 0 .../rpc/ServerPluginsMarketplacesApi.java | 0 .../copilot/generated/rpc/ServerRpc.java | 0 .../generated/rpc/ServerRuntimeApi.java | 0 .../generated/rpc/ServerSecretsApi.java | 0 .../generated/rpc/ServerSessionFsApi.java | 0 .../generated/rpc/ServerSessionsApi.java | 0 .../copilot/generated/rpc/ServerSkill.java | 0 .../generated/rpc/ServerSkillsApi.java | 0 .../generated/rpc/ServerSkillsConfigApi.java | 0 .../copilot/generated/rpc/ServerToolsApi.java | 0 .../copilot/generated/rpc/ServerUserApi.java | 0 .../generated/rpc/ServerUserSettingsApi.java | 0 .../generated/rpc/SessionAbortParams.java | 0 .../generated/rpc/SessionAbortResult.java | 0 .../generated/rpc/SessionAgentApi.java | 0 .../rpc/SessionAgentDeselectParams.java | 0 .../rpc/SessionAgentGetCurrentParams.java | 0 .../rpc/SessionAgentGetCurrentResult.java | 0 .../generated/rpc/SessionAgentListParams.java | 0 .../generated/rpc/SessionAgentListResult.java | 0 .../rpc/SessionAgentReloadParams.java | 0 .../rpc/SessionAgentReloadResult.java | 0 .../rpc/SessionAgentSelectParams.java | 0 .../rpc/SessionAgentSelectResult.java | 0 .../rpc/SessionAgentSetPromptParams.java | 0 ...essionCancelAllBackgroundAgentsParams.java | 0 .../generated/rpc/SessionCanvasActionApi.java | 0 .../rpc/SessionCanvasActionInvokeParams.java | 0 .../rpc/SessionCanvasActionInvokeResult.java | 0 .../generated/rpc/SessionCanvasApi.java | 0 .../rpc/SessionCanvasCloseParams.java | 0 .../rpc/SessionCanvasListOpenParams.java | 0 .../rpc/SessionCanvasListOpenResult.java | 0 .../rpc/SessionCanvasListParams.java | 0 .../rpc/SessionCanvasListResult.java | 0 .../rpc/SessionCanvasOpenParams.java | 0 .../rpc/SessionCanvasOpenResult.java | 0 .../generated/rpc/SessionCapability.java | 0 .../generated/rpc/SessionCommandsApi.java | 0 .../rpc/SessionCommandsEnqueueParams.java | 0 .../rpc/SessionCommandsEnqueueResult.java | 0 .../rpc/SessionCommandsExecuteParams.java | 0 .../rpc/SessionCommandsExecuteResult.java | 0 ...ionCommandsHandlePendingCommandParams.java | 0 ...ionCommandsHandlePendingCommandResult.java | 0 .../rpc/SessionCommandsInvokeParams.java | 0 .../rpc/SessionCommandsListParams.java | 0 .../rpc/SessionCommandsListResult.java | 0 ...nCommandsRespondToQueuedCommandParams.java | 0 ...nCommandsRespondToQueuedCommandResult.java | 0 .../generated/rpc/SessionCompletionItem.java | 0 .../generated/rpc/SessionCompletionsApi.java | 0 ...CompletionsGetTriggerCharactersParams.java | 0 ...CompletionsGetTriggerCharactersResult.java | 0 .../rpc/SessionCompletionsRequestParams.java | 0 .../rpc/SessionCompletionsRequestResult.java | 0 .../rpc/SessionContentExclusionApi.java | 0 ...ssionContentExclusionCheckPathsParams.java | 0 ...ssionContentExclusionCheckPathsResult.java | 0 .../copilot/generated/rpc/SessionContext.java | 0 .../generated/rpc/SessionContextHostType.java | 0 .../generated/rpc/SessionDebugApi.java | 0 .../rpc/SessionDebugCollectLogsParams.java | 0 .../rpc/SessionDebugCollectLogsResult.java | 0 .../generated/rpc/SessionEventLogApi.java | 0 .../rpc/SessionEventLogReadParams.java | 0 .../rpc/SessionEventLogReadResult.java | 0 ...SessionEventLogRegisterInterestParams.java | 0 ...SessionEventLogRegisterInterestResult.java | 0 .../SessionEventLogReleaseInterestParams.java | 0 .../SessionEventLogReleaseInterestResult.java | 0 .../rpc/SessionEventLogTailParams.java | 0 .../rpc/SessionEventLogTailResult.java | 0 .../generated/rpc/SessionExtensionsApi.java | 0 .../rpc/SessionExtensionsDisableParams.java | 0 .../rpc/SessionExtensionsEnableParams.java | 0 .../rpc/SessionExtensionsListParams.java | 0 .../rpc/SessionExtensionsListResult.java | 0 .../rpc/SessionExtensionsReloadParams.java | 0 ...ensionsSendAttachmentsToMessageParams.java | 0 .../rpc/SessionFactoryAgentParams.java | 0 .../rpc/SessionFactoryAgentResult.java | 0 .../generated/rpc/SessionFactoryApi.java | 0 .../rpc/SessionFactoryCancelParams.java | 0 .../rpc/SessionFactoryCancelResult.java | 0 .../rpc/SessionFactoryGetRunDetailParams.java | 0 .../rpc/SessionFactoryGetRunDetailResult.java | 0 .../rpc/SessionFactoryGetRunParams.java | 0 .../SessionFactoryGetRunProgressParams.java | 0 .../SessionFactoryGetRunProgressResult.java | 0 .../rpc/SessionFactoryGetRunResult.java | 0 .../rpc/SessionFactoryJournalApi.java | 0 .../rpc/SessionFactoryJournalGetParams.java | 0 .../rpc/SessionFactoryJournalGetResult.java | 0 .../rpc/SessionFactoryJournalPutParams.java | 0 .../rpc/SessionFactoryListRunsParams.java | 0 .../rpc/SessionFactoryListRunsResult.java | 0 .../rpc/SessionFactoryLogParams.java | 0 .../rpc/SessionFactoryResumeParams.java | 0 .../rpc/SessionFactoryResumeResult.java | 0 .../rpc/SessionFactoryRunParams.java | 0 .../rpc/SessionFactoryRunResult.java | 0 .../generated/rpc/SessionFleetApi.java | 0 .../rpc/SessionFleetStartParams.java | 0 .../rpc/SessionFleetStartResult.java | 0 .../rpc/SessionFsAppendFileParams.java | 0 .../copilot/generated/rpc/SessionFsError.java | 0 .../generated/rpc/SessionFsErrorCode.java | 0 .../generated/rpc/SessionFsExistsParams.java | 0 .../generated/rpc/SessionFsExistsResult.java | 0 .../generated/rpc/SessionFsMkdirParams.java | 0 .../rpc/SessionFsReadFileParams.java | 0 .../rpc/SessionFsReadFileResult.java | 0 .../generated/rpc/SessionFsReaddirParams.java | 0 .../generated/rpc/SessionFsReaddirResult.java | 0 .../rpc/SessionFsReaddirWithTypesEntry.java | 0 .../SessionFsReaddirWithTypesEntryType.java | 0 .../rpc/SessionFsReaddirWithTypesParams.java | 0 .../rpc/SessionFsReaddirWithTypesResult.java | 0 .../generated/rpc/SessionFsRenameParams.java | 0 .../generated/rpc/SessionFsRmParams.java | 0 .../rpc/SessionFsSetProviderCapabilities.java | 0 .../rpc/SessionFsSetProviderConventions.java | 0 .../rpc/SessionFsSetProviderParams.java | 0 .../rpc/SessionFsSetProviderResult.java | 0 .../rpc/SessionFsSqliteExistsParams.java | 0 .../rpc/SessionFsSqliteExistsResult.java | 0 .../rpc/SessionFsSqliteQueryParams.java | 0 .../rpc/SessionFsSqliteQueryResult.java | 0 .../rpc/SessionFsSqliteQueryType.java | 0 .../rpc/SessionFsSqliteTransactionError.java | 0 .../SessionFsSqliteTransactionErrorClass.java | 0 .../rpc/SessionFsSqliteTransactionParams.java | 0 .../rpc/SessionFsSqliteTransactionResult.java | 0 .../SessionFsSqliteTransactionStatement.java | 0 .../generated/rpc/SessionFsStatParams.java | 0 .../generated/rpc/SessionFsStatResult.java | 0 .../rpc/SessionFsWriteFileParams.java | 0 .../generated/rpc/SessionGitHubAuthApi.java | 0 .../rpc/SessionGitHubAuthGetStatusParams.java | 0 .../rpc/SessionGitHubAuthGetStatusResult.java | 0 ...SessionGitHubAuthSetCredentialsParams.java | 0 ...SessionGitHubAuthSetCredentialsResult.java | 0 ...ionHistoryAbortManualCompactionParams.java | 0 ...ionHistoryAbortManualCompactionResult.java | 0 .../generated/rpc/SessionHistoryApi.java | 0 ...storyCancelBackgroundCompactionParams.java | 0 ...storyCancelBackgroundCompactionResult.java | 0 .../rpc/SessionHistoryClearContextParams.java | 0 .../rpc/SessionHistoryClearContextResult.java | 0 .../rpc/SessionHistoryCompactParams.java | 0 .../rpc/SessionHistoryCompactResult.java | 0 .../SessionHistoryListRewindPointsParams.java | 0 .../SessionHistoryListRewindPointsResult.java | 0 .../SessionHistoryPreviewRewindParams.java | 0 .../SessionHistoryPreviewRewindResult.java | 0 .../rpc/SessionHistoryRewindParams.java | 0 .../rpc/SessionHistoryRewindResult.java | 0 ...ssionHistorySummarizeForHandoffParams.java | 0 ...ssionHistorySummarizeForHandoffResult.java | 0 .../rpc/SessionHistoryTruncateParams.java | 0 .../rpc/SessionHistoryTruncateResult.java | 0 .../generated/rpc/SessionInstalledPlugin.java | 0 .../generated/rpc/SessionInstructionsApi.java | 0 .../SessionInstructionsGetSourcesParams.java | 0 .../SessionInstructionsGetSourcesResult.java | 0 .../rpc/SessionInterruptMainTurnParams.java | 0 .../rpc/SessionInterruptMainTurnResult.java | 0 .../rpc/SessionLimitPredictionApi.java | 0 .../SessionLimitPredictionBaselineData.java | 0 .../rpc/SessionLimitPredictionClientType.java | 0 .../rpc/SessionLimitPredictionDetails.java | 0 .../SessionLimitPredictionPredictParams.java | 0 .../rpc/SessionLimitPredictionResult.java | 0 ...SessionLimitPredictionResultAvailable.java | 0 ...ssionLimitPredictionResultUnavailable.java | 0 .../rpc/SessionLimitPredictionSource.java | 0 .../rpc/SessionLimitPredictionTier.java | 0 .../rpc/SessionLimitPredictionTierOption.java | 0 ...ssionLimitPredictionUnavailableReason.java | 0 .../generated/rpc/SessionLimitsConfig.java | 0 .../generated/rpc/SessionListFilter.java | 0 .../generated/rpc/SessionLogLevel.java | 0 .../generated/rpc/SessionLogParams.java | 0 .../generated/rpc/SessionLogResult.java | 0 .../copilot/generated/rpc/SessionLspApi.java | 0 .../rpc/SessionLspInitializeParams.java | 0 .../rpc/SessionManagedPermissions.java | 0 .../generated/rpc/SessionManagedSettings.java | 0 .../copilot/generated/rpc/SessionMcpApi.java | 0 .../generated/rpc/SessionMcpAppsApi.java | 0 .../rpc/SessionMcpAppsCallToolParams.java | 0 .../rpc/SessionMcpAppsDiagnoseParams.java | 0 .../rpc/SessionMcpAppsDiagnoseResult.java | 0 .../SessionMcpAppsGetHostContextParams.java | 0 .../SessionMcpAppsGetHostContextResult.java | 0 .../rpc/SessionMcpAppsListToolsParams.java | 0 .../rpc/SessionMcpAppsListToolsResult.java | 0 .../rpc/SessionMcpAppsReadResourceParams.java | 0 .../rpc/SessionMcpAppsReadResourceResult.java | 0 .../SessionMcpAppsSetHostContextParams.java | 0 ...ssionMcpCancelSamplingExecutionParams.java | 0 ...ssionMcpCancelSamplingExecutionResult.java | 0 .../rpc/SessionMcpConfigureGitHubParams.java | 0 .../rpc/SessionMcpConfigureGitHubResult.java | 0 .../rpc/SessionMcpDisableParams.java | 0 .../generated/rpc/SessionMcpEnableParams.java | 0 .../rpc/SessionMcpExecuteSamplingParams.java | 0 .../rpc/SessionMcpExecuteSamplingResult.java | 0 .../generated/rpc/SessionMcpHeadersApi.java | 0 ...dlePendingHeadersRefreshRequestParams.java | 0 ...dlePendingHeadersRefreshRequestResult.java | 0 .../rpc/SessionMcpIsServerRunningParams.java | 0 .../rpc/SessionMcpIsServerRunningResult.java | 0 .../generated/rpc/SessionMcpListParams.java | 0 .../generated/rpc/SessionMcpListResult.java | 0 .../rpc/SessionMcpListToolsParams.java | 0 .../rpc/SessionMcpListToolsResult.java | 0 .../generated/rpc/SessionMcpOauthApi.java | 0 ...OauthAuthenticationStateChangedParams.java | 0 ...ionMcpOauthHandlePendingRequestParams.java | 0 ...ionMcpOauthHandlePendingRequestResult.java | 0 .../rpc/SessionMcpOauthLoginParams.java | 0 .../rpc/SessionMcpOauthLoginResult.java | 0 .../rpc/SessionMcpOauthRespondParams.java | 0 .../rpc/SessionMcpOauthRespondResult.java | 0 ...essionMcpRegisterExternalClientParams.java | 0 .../generated/rpc/SessionMcpReloadParams.java | 0 .../rpc/SessionMcpReloadWithConfigParams.java | 0 .../rpc/SessionMcpReloadWithConfigResult.java | 0 .../rpc/SessionMcpRemoveGitHubParams.java | 0 .../rpc/SessionMcpRemoveGitHubResult.java | 0 .../generated/rpc/SessionMcpResourcesApi.java | 0 .../rpc/SessionMcpResourcesListParams.java | 0 .../rpc/SessionMcpResourcesListResult.java | 0 ...essionMcpResourcesListTemplatesParams.java | 0 ...essionMcpResourcesListTemplatesResult.java | 0 .../rpc/SessionMcpResourcesReadParams.java | 0 .../rpc/SessionMcpResourcesReadResult.java | 0 .../rpc/SessionMcpRestartServerParams.java | 0 .../rpc/SessionMcpSetEnvValueModeParams.java | 0 .../rpc/SessionMcpSetEnvValueModeResult.java | 0 .../rpc/SessionMcpStartServerParams.java | 0 .../rpc/SessionMcpStopServerParams.java | 0 ...sionMcpUnregisterExternalClientParams.java | 0 .../rpc/SessionMetadataActivityParams.java | 0 .../rpc/SessionMetadataActivityResult.java | 0 .../generated/rpc/SessionMetadataApi.java | 0 .../rpc/SessionMetadataContextInfoParams.java | 0 .../rpc/SessionMetadataContextInfoResult.java | 0 ...onMetadataGetContextAttributionParams.java | 0 ...onMetadataGetContextAttributionResult.java | 0 ...adataGetContextHeaviestMessagesParams.java | 0 ...adataGetContextHeaviestMessagesResult.java | 0 .../SessionMetadataIsProcessingParams.java | 0 .../SessionMetadataIsProcessingResult.java | 0 ...nMetadataRecomputeContextTokensParams.java | 0 ...nMetadataRecomputeContextTokensResult.java | 0 ...sionMetadataRecordContextChangeParams.java | 0 ...sionMetadataSetWorkingDirectoryParams.java | 0 ...sionMetadataSetWorkingDirectoryResult.java | 0 .../rpc/SessionMetadataSnapshotParams.java | 0 .../rpc/SessionMetadataSnapshotResult.java | 0 .../copilot/generated/rpc/SessionMode.java | 0 .../copilot/generated/rpc/SessionModeApi.java | 0 .../generated/rpc/SessionModeGetParams.java | 0 .../generated/rpc/SessionModeSetParams.java | 0 .../generated/rpc/SessionModelApi.java | 0 .../rpc/SessionModelGetCurrentParams.java | 0 .../rpc/SessionModelGetCurrentResult.java | 0 .../generated/rpc/SessionModelListParams.java | 0 .../generated/rpc/SessionModelListResult.java | 0 .../rpc/SessionModelPriceCategory.java | 0 .../SessionModelSetReasoningEffortParams.java | 0 .../SessionModelSetReasoningEffortResult.java | 0 .../rpc/SessionModelSwitchToParams.java | 0 .../rpc/SessionModelSwitchToResult.java | 0 .../copilot/generated/rpc/SessionNameApi.java | 0 .../generated/rpc/SessionNameGetParams.java | 0 .../generated/rpc/SessionNameGetResult.java | 0 .../rpc/SessionNameSetAutoParams.java | 0 .../rpc/SessionNameSetAutoResult.java | 0 .../generated/rpc/SessionNameSetParams.java | 0 .../generated/rpc/SessionOpenOptions.java | 0 ...tionsAdditionalContentExclusionPolicy.java | 0 ...sAdditionalContentExclusionPolicyRule.java | 0 ...ionalContentExclusionPolicyRuleSource.java | 0 ...AdditionalContentExclusionPolicyScope.java | 0 .../rpc/SessionOpenOptionsEnvValueMode.java | 0 .../SessionOpenOptionsReasoningSummary.java | 0 .../generated/rpc/SessionOptionsApi.java | 0 .../rpc/SessionOptionsUpdateParams.java | 0 .../rpc/SessionOptionsUpdateResult.java | 0 .../generated/rpc/SessionPermissionsApi.java | 0 .../SessionPermissionsConfigureParams.java | 0 .../SessionPermissionsConfigureResult.java | 0 ...ermissionsFolderTrustAddTrustedParams.java | 0 ...ermissionsFolderTrustAddTrustedResult.java | 0 .../rpc/SessionPermissionsFolderTrustApi.java | 0 ...PermissionsFolderTrustIsTrustedParams.java | 0 ...PermissionsFolderTrustIsTrustedResult.java | 0 .../SessionPermissionsGetAllowAllParams.java | 0 .../SessionPermissionsGetAllowAllResult.java | 0 ...sHandlePendingPermissionRequestParams.java | 0 ...sHandlePendingPermissionRequestResult.java | 0 ...issionsLocationsAddToolApprovalParams.java | 0 ...issionsLocationsAddToolApprovalResult.java | 0 .../rpc/SessionPermissionsLocationsApi.java | 0 ...essionPermissionsLocationsApplyParams.java | 0 ...essionPermissionsLocationsApplyResult.java | 0 ...sionPermissionsLocationsResolveParams.java | 0 ...sionPermissionsLocationsResolveResult.java | 0 .../SessionPermissionsModifyRulesParams.java | 0 .../SessionPermissionsModifyRulesResult.java | 0 ...ionPermissionsNotifyPromptShownParams.java | 0 ...ionPermissionsNotifyPromptShownResult.java | 0 .../rpc/SessionPermissionsPathsAddParams.java | 0 .../rpc/SessionPermissionsPathsAddResult.java | 0 .../rpc/SessionPermissionsPathsApi.java | 0 ...sIsPathWithinAllowedDirectoriesParams.java | 0 ...sIsPathWithinAllowedDirectoriesResult.java | 0 ...sionsPathsIsPathWithinWorkspaceParams.java | 0 ...sionsPathsIsPathWithinWorkspaceResult.java | 0 .../SessionPermissionsPathsListParams.java | 0 .../SessionPermissionsPathsListResult.java | 0 ...onPermissionsPathsUpdatePrimaryParams.java | 0 ...onPermissionsPathsUpdatePrimaryResult.java | 0 ...ssionPermissionsPendingRequestsParams.java | 0 ...ssionPermissionsPendingRequestsResult.java | 0 ...ermissionsResetSessionApprovalsParams.java | 0 ...ermissionsResetSessionApprovalsResult.java | 0 .../SessionPermissionsSetAllowAllParams.java | 0 .../SessionPermissionsSetAllowAllResult.java | 0 ...SessionPermissionsSetApproveAllParams.java | 0 ...SessionPermissionsSetApproveAllResult.java | 0 .../SessionPermissionsSetRequiredParams.java | 0 .../SessionPermissionsSetRequiredResult.java | 0 .../rpc/SessionPermissionsUrlsApi.java | 0 ...missionsUrlsSetUnrestrictedModeParams.java | 0 ...missionsUrlsSetUnrestrictedModeResult.java | 0 .../copilot/generated/rpc/SessionPlanApi.java | 0 .../rpc/SessionPlanDeleteParams.java | 0 .../generated/rpc/SessionPlanReadParams.java | 0 .../generated/rpc/SessionPlanReadResult.java | 0 .../rpc/SessionPlanReadSqlTodosParams.java | 0 .../rpc/SessionPlanReadSqlTodosResult.java | 0 ...lanReadSqlTodosWithDependenciesParams.java | 0 ...lanReadSqlTodosWithDependenciesResult.java | 0 .../rpc/SessionPlanUpdateParams.java | 0 .../generated/rpc/SessionPluginsApi.java | 0 .../rpc/SessionPluginsListParams.java | 0 .../rpc/SessionPluginsListResult.java | 0 .../rpc/SessionPluginsReloadParams.java | 0 .../rpc/SessionProviderAddParams.java | 0 .../rpc/SessionProviderAddResult.java | 0 .../generated/rpc/SessionProviderApi.java | 0 .../rpc/SessionProviderGetEndpointParams.java | 0 .../rpc/SessionProviderGetEndpointResult.java | 0 .../generated/rpc/SessionQueueApi.java | 0 ...sionQueueBeginDeferredIdleDrainParams.java | 0 ...sionQueueBeginDeferredIdleDrainResult.java | 0 .../rpc/SessionQueueClearParams.java | 0 ...QueueConsumeSystemNotificationsParams.java | 0 ...QueueConsumeSystemNotificationsResult.java | 0 .../SessionQueueDeferSessionIdleParams.java | 0 .../rpc/SessionQueueDuplicateAtParams.java | 0 .../rpc/SessionQueueDuplicateAtResult.java | 0 ...essionQueueEnqueueResumePendingParams.java | 0 ...essionQueueEnqueueResumePendingResult.java | 0 ...ionQueueFinishDeferredIdleDrainParams.java | 0 ...ionQueueFinishDeferredIdleDrainResult.java | 0 .../rpc/SessionQueueHasPendingParams.java | 0 .../rpc/SessionQueueHasPendingResult.java | 0 .../rpc/SessionQueueInsertAtParams.java | 0 .../rpc/SessionQueueInsertAtResult.java | 0 .../rpc/SessionQueueMoveItemParams.java | 0 .../rpc/SessionQueueMoveItemResult.java | 0 .../rpc/SessionQueuePendingItemsParams.java | 0 .../rpc/SessionQueuePendingItemsResult.java | 0 .../rpc/SessionQueueProcessParams.java | 0 .../rpc/SessionQueueRemoveAtParams.java | 0 .../rpc/SessionQueueRemoveAtResult.java | 0 .../SessionQueueRemoveMostRecentParams.java | 0 .../SessionQueueRemoveMostRecentResult.java | 0 .../rpc/SessionQueueSendNowParams.java | 0 .../rpc/SessionQueueSendNowResult.java | 0 .../rpc/SessionQueueSetDrainPausedParams.java | 0 .../rpc/SessionQueueSnapshotParams.java | 0 .../rpc/SessionQueueSnapshotResult.java | 0 .../rpc/SessionQueueUpdateTextParams.java | 0 .../rpc/SessionQueueUpdateTextResult.java | 0 .../generated/rpc/SessionRemoteApi.java | 0 .../rpc/SessionRemoteDisableParams.java | 0 .../rpc/SessionRemoteEnableParams.java | 0 .../rpc/SessionRemoteEnableResult.java | 0 ...ionRemoteNotifySteerableChangedParams.java | 0 .../copilot/generated/rpc/SessionRpc.java | 0 .../rpc/SessionScheduleAddAtParams.java | 0 .../rpc/SessionScheduleAddAtResult.java | 0 .../rpc/SessionScheduleAddCronParams.java | 0 .../rpc/SessionScheduleAddCronResult.java | 0 .../rpc/SessionScheduleAddParams.java | 0 .../rpc/SessionScheduleAddResult.java | 0 .../SessionScheduleAddSelfPacedParams.java | 0 .../SessionScheduleAddSelfPacedResult.java | 0 .../generated/rpc/SessionScheduleApi.java | 0 .../SessionScheduleHasSelfPacedParams.java | 0 .../SessionScheduleHasSelfPacedResult.java | 0 .../rpc/SessionScheduleHydrateParams.java | 0 .../rpc/SessionScheduleListParams.java | 0 .../rpc/SessionScheduleListResult.java | 0 .../SessionScheduleRearmSelfPacedParams.java | 0 .../SessionScheduleRearmSelfPacedResult.java | 0 .../rpc/SessionScheduleStopParams.java | 0 .../rpc/SessionScheduleStopResult.java | 0 .../rpc/SessionSendMessagesParams.java | 0 .../rpc/SessionSendMessagesResult.java | 0 .../generated/rpc/SessionSendParams.java | 0 .../generated/rpc/SessionSendResult.java | 0 .../SessionSendSystemNotificationParams.java | 0 .../generated/rpc/SessionSettingsApi.java | 0 ...ttingsBuiltInToolAvailabilitySnapshot.java | 0 ...essionSettingsEvaluatePredicateParams.java | 0 ...essionSettingsEvaluatePredicateResult.java | 0 .../rpc/SessionSettingsJobSnapshot.java | 0 .../rpc/SessionSettingsModelSnapshot.java | 0 ...ssionSettingsOnlineEvaluationSnapshot.java | 0 .../rpc/SessionSettingsPredicateName.java | 0 .../rpc/SessionSettingsRepoSnapshot.java | 0 .../rpc/SessionSettingsSnapshotParams.java | 0 .../rpc/SessionSettingsSnapshotResult.java | 0 .../SessionSettingsValidationSnapshot.java | 0 .../generated/rpc/SessionShellApi.java | 0 ...SessionShellCancelUserRequestedParams.java | 0 ...SessionShellCancelUserRequestedResult.java | 0 .../generated/rpc/SessionShellExecParams.java | 0 .../generated/rpc/SessionShellExecResult.java | 0 ...essionShellExecuteUserRequestedParams.java | 0 ...essionShellExecuteUserRequestedResult.java | 0 .../generated/rpc/SessionShellKillParams.java | 0 .../generated/rpc/SessionShellKillResult.java | 0 .../generated/rpc/SessionShutdownParams.java | 0 .../generated/rpc/SessionSkillsApi.java | 0 .../rpc/SessionSkillsDisableParams.java | 0 .../rpc/SessionSkillsEnableParams.java | 0 .../rpc/SessionSkillsEnsureLoadedParams.java | 0 .../rpc/SessionSkillsGetInvokedParams.java | 0 .../rpc/SessionSkillsGetInvokedResult.java | 0 .../rpc/SessionSkillsListParams.java | 0 .../rpc/SessionSkillsListResult.java | 0 .../rpc/SessionSkillsReloadParams.java | 0 .../rpc/SessionSkillsReloadResult.java | 0 .../copilot/generated/rpc/SessionSource.java | 0 .../generated/rpc/SessionSuspendParams.java | 0 .../generated/rpc/SessionTasksApi.java | 0 .../rpc/SessionTasksCancelParams.java | 0 .../rpc/SessionTasksCancelResult.java | 0 ...essionTasksGetCurrentPromotableParams.java | 0 ...essionTasksGetCurrentPromotableResult.java | 0 .../rpc/SessionTasksGetProgressParams.java | 0 .../rpc/SessionTasksGetProgressResult.java | 0 .../generated/rpc/SessionTasksListParams.java | 0 .../generated/rpc/SessionTasksListResult.java | 0 ...TasksPromoteCurrentToBackgroundParams.java | 0 ...TasksPromoteCurrentToBackgroundResult.java | 0 ...SessionTasksPromoteToBackgroundParams.java | 0 ...SessionTasksPromoteToBackgroundResult.java | 0 .../rpc/SessionTasksRefreshParams.java | 0 .../rpc/SessionTasksRemoveParams.java | 0 .../rpc/SessionTasksRemoveResult.java | 0 .../rpc/SessionTasksSendMessageParams.java | 0 .../rpc/SessionTasksSendMessageResult.java | 0 .../rpc/SessionTasksStartAgentParams.java | 0 .../rpc/SessionTasksStartAgentResult.java | 0 .../rpc/SessionTasksWaitForPendingParams.java | 0 .../generated/rpc/SessionTelemetryApi.java | 0 ...SessionTelemetryGetEngagementIdParams.java | 0 ...SessionTelemetryGetEngagementIdResult.java | 0 ...ionTelemetrySetFeatureOverridesParams.java | 0 .../generated/rpc/SessionToolsApi.java | 0 .../SessionToolsGetCurrentMetadataParams.java | 0 .../SessionToolsGetCurrentMetadataResult.java | 0 ...ssionToolsHandlePendingToolCallParams.java | 0 ...ssionToolsHandlePendingToolCallResult.java | 0 ...ssionToolsInitializeAndValidateParams.java | 0 ...sionToolsUpdateSubagentSettingsParams.java | 0 .../copilot/generated/rpc/SessionUiApi.java | 0 .../rpc/SessionUiElicitationParams.java | 0 .../rpc/SessionUiElicitationResult.java | 0 .../rpc/SessionUiEphemeralQueryParams.java | 0 .../rpc/SessionUiEphemeralQueryResult.java | 0 ...onUiHandlePendingAutoModeSwitchParams.java | 0 ...onUiHandlePendingAutoModeSwitchResult.java | 0 ...ssionUiHandlePendingElicitationParams.java | 0 ...ssionUiHandlePendingElicitationResult.java | 0 ...sionUiHandlePendingExitPlanModeParams.java | 0 ...sionUiHandlePendingExitPlanModeResult.java | 0 .../SessionUiHandlePendingSamplingParams.java | 0 .../SessionUiHandlePendingSamplingResult.java | 0 ...lePendingSessionLimitsExhaustedParams.java | 0 ...lePendingSessionLimitsExhaustedResult.java | 0 ...SessionUiHandlePendingUserInputParams.java | 0 ...SessionUiHandlePendingUserInputResult.java | 0 ...sterDirectAutoModeSwitchHandlerParams.java | 0 ...sterDirectAutoModeSwitchHandlerResult.java | 0 ...sterDirectAutoModeSwitchHandlerParams.java | 0 ...sterDirectAutoModeSwitchHandlerResult.java | 0 .../generated/rpc/SessionUsageApi.java | 0 .../rpc/SessionUsageGetMetricsParams.java | 0 .../rpc/SessionUsageGetMetricsResult.java | 0 .../generated/rpc/SessionVisibilityApi.java | 0 .../rpc/SessionVisibilityGetParams.java | 0 .../rpc/SessionVisibilityGetResult.java | 0 .../rpc/SessionVisibilitySetParams.java | 0 .../rpc/SessionVisibilitySetResult.java | 0 .../rpc/SessionVisibilityStatus.java | 0 .../rpc/SessionWorkingDirectoryContext.java | 0 ...essionWorkingDirectoryContextHostType.java | 0 .../SessionWorkspacesAddSummaryParams.java | 0 .../SessionWorkspacesAddSummaryResult.java | 0 .../generated/rpc/SessionWorkspacesApi.java | 0 ...kspacesAutopilotObjectiveExistsParams.java | 0 ...kspacesAutopilotObjectiveExistsResult.java | 0 .../SessionWorkspacesCreateFileParams.java | 0 ...kspacesDeleteAutopilotObjectiveParams.java | 0 ...kspacesDeleteAutopilotObjectiveResult.java | 0 .../rpc/SessionWorkspacesDiffParams.java | 0 .../rpc/SessionWorkspacesDiffResult.java | 0 .../rpc/SessionWorkspacesEnsureParams.java | 0 .../rpc/SessionWorkspacesEnsureResult.java | 0 .../SessionWorkspacesGetWorkspaceParams.java | 0 .../SessionWorkspacesGetWorkspaceResult.java | 0 ...essionWorkspacesListCheckpointsParams.java | 0 ...essionWorkspacesListCheckpointsResult.java | 0 .../rpc/SessionWorkspacesListFilesParams.java | 0 .../rpc/SessionWorkspacesListFilesResult.java | 0 ...orkspacesReadAutopilotObjectiveParams.java | 0 ...orkspacesReadAutopilotObjectiveResult.java | 0 ...SessionWorkspacesReadCheckpointParams.java | 0 ...SessionWorkspacesReadCheckpointResult.java | 0 .../rpc/SessionWorkspacesReadFileParams.java | 0 .../rpc/SessionWorkspacesReadFileResult.java | 0 ...SessionWorkspacesSaveLargePasteParams.java | 0 ...SessionWorkspacesSaveLargePasteResult.java | 0 ...sionWorkspacesTruncateSummariesParams.java | 0 ...sionWorkspacesTruncateSummariesResult.java | 0 ...SessionWorkspacesUpdateMetadataParams.java | 0 ...SessionWorkspacesUpdateMetadataResult.java | 0 ...rkspacesWriteAutopilotObjectiveParams.java | 0 ...rkspacesWriteAutopilotObjectiveResult.java | 0 .../rpc/SessionsBulkDeleteParams.java | 0 .../rpc/SessionsBulkDeleteResult.java | 0 .../rpc/SessionsCheckInUseParams.java | 0 .../rpc/SessionsCheckInUseResult.java | 0 .../generated/rpc/SessionsCloseParams.java | 0 ...sionsConfigureSessionExtensionsParams.java | 0 .../generated/rpc/SessionsConnectParams.java | 0 .../generated/rpc/SessionsConnectResult.java | 0 .../generated/rpc/SessionsDeleteParams.java | 0 .../rpc/SessionsEnrichMetadataParams.java | 0 .../rpc/SessionsEnrichMetadataResult.java | 0 .../rpc/SessionsFindByPrefixParams.java | 0 .../rpc/SessionsFindByPrefixResult.java | 0 .../rpc/SessionsFindByTaskIdParams.java | 0 .../rpc/SessionsFindByTaskIdResult.java | 0 .../generated/rpc/SessionsForkParams.java | 0 .../generated/rpc/SessionsForkResult.java | 0 .../rpc/SessionsGetBoardEntryCountParams.java | 0 .../rpc/SessionsGetBoardEntryCountResult.java | 0 .../rpc/SessionsGetEventFilePathParams.java | 0 .../rpc/SessionsGetEventFilePathResult.java | 0 .../rpc/SessionsGetLastForContextParams.java | 0 .../rpc/SessionsGetLastForContextResult.java | 0 .../rpc/SessionsGetMetadataParams.java | 0 .../rpc/SessionsGetMetadataResult.java | 0 ...ionsGetPersistedRemoteSteerableParams.java | 0 ...ionsGetPersistedRemoteSteerableResult.java | 0 .../SessionsGetRemoteControlStatusResult.java | 0 .../generated/rpc/SessionsGetSizesResult.java | 0 .../SessionsListNonEmptySessionIdsParams.java | 0 .../SessionsListNonEmptySessionIdsResult.java | 0 .../generated/rpc/SessionsListParams.java | 0 .../generated/rpc/SessionsListResult.java | 0 .../SessionsLoadDeferredRepoHooksParams.java | 0 .../SessionsLoadDeferredRepoHooksResult.java | 0 .../generated/rpc/SessionsOpenAttach.java | 0 .../generated/rpc/SessionsOpenCloud.java | 0 .../generated/rpc/SessionsOpenCreate.java | 0 .../generated/rpc/SessionsOpenHandoff.java | 0 .../rpc/SessionsOpenHandoffTaskType.java | 0 .../generated/rpc/SessionsOpenParams.java | 0 .../generated/rpc/SessionsOpenProgress.java | 0 .../rpc/SessionsOpenProgressStatus.java | 0 .../rpc/SessionsOpenProgressStep.java | 0 .../generated/rpc/SessionsOpenRemote.java | 0 .../generated/rpc/SessionsOpenResult.java | 0 .../generated/rpc/SessionsOpenResume.java | 0 .../generated/rpc/SessionsOpenResumeLast.java | 0 .../generated/rpc/SessionsOpenStatus.java | 0 .../generated/rpc/SessionsPruneOldParams.java | 0 .../generated/rpc/SessionsPruneOldResult.java | 0 ...egisterExtensionToolsOnSessionOptions.java | 0 ...RegisterExtensionToolsOnSessionParams.java | 0 ...RegisterExtensionToolsOnSessionResult.java | 0 .../rpc/SessionsReleaseLockParams.java | 0 .../rpc/SessionsReloadPluginHooksParams.java | 0 .../generated/rpc/SessionsSaveParams.java | 0 .../SessionsSetAdditionalPluginsParams.java | 0 ...essionsSetRemoteControlSteeringParams.java | 0 ...essionsSetRemoteControlSteeringResult.java | 0 .../rpc/SessionsStartRemoteControlParams.java | 0 .../rpc/SessionsStartRemoteControlResult.java | 0 .../rpc/SessionsStopRemoteControlParams.java | 0 .../rpc/SessionsStopRemoteControlResult.java | 0 .../SessionsTransferRemoteControlParams.java | 0 .../SessionsTransferRemoteControlResult.java | 0 .../generated/rpc/ShellInitProfile.java | 0 .../generated/rpc/ShellInitScript.java | 0 .../generated/rpc/ShellInitScriptShell.java | 0 .../generated/rpc/ShellKillSignal.java | 0 .../copilot/generated/rpc/ShellOptions.java | 0 .../copilot/generated/rpc/ShutdownType.java | 0 .../github/copilot/generated/rpc/Skill.java | 0 .../generated/rpc/SkillDiscoveryPath.java | 0 .../generated/rpc/SkillDiscoveryScope.java | 0 .../copilot/generated/rpc/SkillSource.java | 0 .../SkillsConfigSetDisabledSkillsParams.java | 0 .../generated/rpc/SkillsDiscoverParams.java | 0 .../generated/rpc/SkillsDiscoverResult.java | 0 .../rpc/SkillsGetDiscoveryPathsParams.java | 0 .../rpc/SkillsGetDiscoveryPathsResult.java | 0 .../generated/rpc/SkillsInvokedSkill.java | 0 .../rpc/SlashCommandAgentPromptResult.java | 0 .../rpc/SlashCommandCompletedResult.java | 0 .../generated/rpc/SlashCommandInfo.java | 0 .../generated/rpc/SlashCommandInput.java | 0 .../rpc/SlashCommandInputChoice.java | 0 .../rpc/SlashCommandInputCompletion.java | 0 .../rpc/SlashCommandInvocationResult.java | 0 .../generated/rpc/SlashCommandKind.java | 0 .../SlashCommandSelectSubcommandOption.java | 0 .../SlashCommandSelectSubcommandResult.java | 0 .../generated/rpc/SlashCommandTextResult.java | 0 .../generated/rpc/SubagentSettingsEntry.java | 0 .../rpc/SubagentSettingsEntryContextTier.java | 0 .../github/copilot/generated/rpc/Tool.java | 0 .../generated/rpc/ToolsListParams.java | 0 .../generated/rpc/ToolsListResult.java | 0 .../rpc/UIAutoModeSwitchResponse.java | 0 .../generated/rpc/UIElicitationResponse.java | 0 .../rpc/UIElicitationResponseAction.java | 0 .../generated/rpc/UIElicitationSchema.java | 0 .../generated/rpc/UIExitPlanModeAction.java | 0 .../generated/rpc/UIExitPlanModeResponse.java | 0 .../rpc/UIHandlePendingSamplingResponse.java | 0 .../rpc/UISessionLimitsExhaustedResponse.java | 0 ...ISessionLimitsExhaustedResponseAction.java | 0 .../generated/rpc/UIUserInputResponse.java | 0 .../rpc/UsageMetricsCodeChanges.java | 0 .../rpc/UsageMetricsModelMetric.java | 0 .../rpc/UsageMetricsModelMetricRequests.java | 0 .../UsageMetricsModelMetricTokenDetail.java | 0 .../rpc/UsageMetricsModelMetricUsage.java | 0 .../rpc/UsageMetricsTokenDetail.java | 0 .../generated/rpc/UserSettingMetadata.java | 0 .../generated/rpc/UserSettingsGetResult.java | 0 .../generated/rpc/UserSettingsSetParams.java | 0 .../generated/rpc/UserSettingsSetResult.java | 0 .../copilot/generated/rpc/Verbosity.java | 0 .../rpc/WorkspaceDiffFileChange.java | 0 .../rpc/WorkspaceDiffFileChangeType.java | 0 .../generated/rpc/WorkspaceDiffMode.java | 0 .../rpc/WorkspaceSummaryHostType.java | 0 .../generated/rpc/WorkspacesCheckpoints.java | 0 .../WorkspacesWorkspaceDetailsHostType.java | 0 .../copilot/generated/rpc/package-info.java | 0 .../copilot/AllowCopilotExperimental.java | 0 .../com/github/copilot/CliServerManager.java | 0 .../com/github/copilot/ConnectionState.java | 0 .../com/github/copilot/CopilotClient.java | 328 ++++++- .../github/copilot/CopilotExperimental.java | 0 .../copilot/CopilotExperimentalProcessor.java | 0 .../github/copilot/CopilotRequestContext.java | 0 .../github/copilot/CopilotRequestHandler.java | 0 .../copilot/CopilotRequestTransport.java | 0 .../com/github/copilot/CopilotSession.java | 0 .../copilot/CopilotWebSocketCloseStatus.java | 0 .../copilot/CopilotWebSocketForwarder.java | 0 .../copilot/CopilotWebSocketHandler.java | 0 .../copilot/CopilotWebSocketMessage.java | 0 .../com/github/copilot/EventErrorHandler.java | 0 .../com/github/copilot/EventErrorPolicy.java | 0 .../github/copilot/ExtractedTransforms.java | 0 .../copilot/GitHubTelemetryAdapter.java | 0 .../copilot/InternalExecutorProvider.java | 0 .../com/github/copilot/JsonRpcClient.java | 42 + .../com/github/copilot/JsonRpcException.java | 0 .../github/copilot/LifecycleEventManager.java | 0 .../github/copilot/LlmInferenceAdapter.java | 0 .../github/copilot/LlmInferenceExchange.java | 0 .../copilot/LlmWebSocketResponseBridge.java | 0 .../com/github/copilot/LoggingHelpers.java | 0 .../github/copilot/RpcHandlerDispatcher.java | 0 .../github/copilot/SdkProtocolVersion.java | 0 .../github/copilot/SessionRequestBuilder.java | 0 .../com/github/copilot/SystemMessageMode.java | 0 .../github/copilot/ffi/FfiOutputStream.java | 63 ++ .../github/copilot/ffi/FfiRuntimeHost.java | 347 ++++++++ .../github/copilot/ffi/JnaNativeBinding.java | 295 +++++++ .../com/github/copilot/ffi/NativeBinding.java | 131 +++ .../copilot/ffi/NativeRuntimeLoader.java | 504 +++++++++++ .../github/copilot/ffi/OutboundCallback.java | 46 + .../github/copilot/ffi/PlatformDetector.java | 303 +++++++ .../github/copilot/ffi/QueueInputStream.java | 119 +++ .../copilot/ffi/ReaderThreadFactory.java | 22 + .../java/com/github/copilot/ffi/SizeT.java | 39 + .../java/com/github/copilot/package-info.java | 0 .../com/github/copilot/rpc/AgentInfo.java | 0 .../com/github/copilot/rpc/AgentMode.java | 0 .../github/copilot/rpc/AgentStopHandler.java | 0 .../copilot/rpc/AgentStopHookInput.java | 0 .../copilot/rpc/AgentStopHookOutput.java | 0 .../com/github/copilot/rpc/Attachment.java | 0 .../copilot/rpc/AutoModeSwitchHandler.java | 0 .../copilot/rpc/AutoModeSwitchInvocation.java | 0 .../copilot/rpc/AutoModeSwitchRequest.java | 0 .../copilot/rpc/AutoModeSwitchResponse.java | 0 .../com/github/copilot/rpc/AzureOptions.java | 0 .../copilot/rpc/BearerTokenProvider.java | 0 .../github/copilot/rpc/BlobAttachment.java | 0 .../com/github/copilot/rpc/BuiltInTools.java | 0 .../copilot/rpc/CapiSessionOptions.java | 0 .../copilot/rpc/CloudSessionOptions.java | 0 .../copilot/rpc/CloudSessionRepository.java | 0 .../github/copilot/rpc/CommandContext.java | 0 .../github/copilot/rpc/CommandDefinition.java | 0 .../github/copilot/rpc/CommandHandler.java | 0 .../copilot/rpc/CommandWireDefinition.java | 0 .../github/copilot/rpc/CopilotClientMode.java | 0 .../copilot/rpc/CopilotClientOptions.java | 43 +- .../rpc/CopilotExpAssignmentResponse.java | 0 .../copilot/rpc/CreateSessionRequest.java | 0 .../copilot/rpc/CreateSessionResponse.java | 0 .../github/copilot/rpc/CustomAgentConfig.java | 0 .../copilot/rpc/DefaultAgentConfig.java | 0 .../copilot/rpc/DeleteSessionResponse.java | 0 .../copilot/rpc/ElicitationContext.java | 0 .../copilot/rpc/ElicitationHandler.java | 0 .../github/copilot/rpc/ElicitationParams.java | 0 .../github/copilot/rpc/ElicitationResult.java | 0 .../copilot/rpc/ElicitationResultAction.java | 0 .../github/copilot/rpc/ElicitationSchema.java | 0 .../copilot/rpc/ExitPlanModeHandler.java | 0 .../copilot/rpc/ExitPlanModeInvocation.java | 0 .../copilot/rpc/ExitPlanModeRequest.java | 0 .../copilot/rpc/ExitPlanModeResult.java | 0 .../github/copilot/rpc/ExpConfigEntry.java | 0 .../copilot/rpc/GetAuthStatusResponse.java | 0 .../rpc/GetForegroundSessionResponse.java | 0 .../copilot/rpc/GetLastSessionIdResponse.java | 0 .../copilot/rpc/GetMessagesResponse.java | 0 .../github/copilot/rpc/GetModelsResponse.java | 0 .../rpc/GetSessionMetadataResponse.java | 0 .../github/copilot/rpc/GetStatusResponse.java | 0 .../copilot/rpc/GitHubMcpToolConfig.java | 0 .../github/copilot/rpc/HookInvocation.java | 0 .../rpc/InProcessRuntimeConnection.java | 27 + .../copilot/rpc/InfiniteSessionConfig.java | 0 .../com/github/copilot/rpc/InputOptions.java | 0 .../com/github/copilot/rpc/JsonRpcError.java | 0 .../github/copilot/rpc/JsonRpcRequest.java | 0 .../github/copilot/rpc/JsonRpcResponse.java | 0 .../copilot/rpc/LargeToolOutputConfig.java | 0 .../copilot/rpc/ListSessionsResponse.java | 0 .../github/copilot/rpc/ManagedSettings.java | 0 .../rpc/ManagedSettingsPermissions.java | 0 .../github/copilot/rpc/McpAuthHandler.java | 0 .../github/copilot/rpc/McpAuthInvocation.java | 0 .../github/copilot/rpc/McpAuthRequest.java | 0 .../com/github/copilot/rpc/McpAuthResult.java | 0 .../com/github/copilot/rpc/McpAuthToken.java | 0 .../copilot/rpc/McpHttpServerConfig.java | 0 .../github/copilot/rpc/McpServerConfig.java | 0 .../copilot/rpc/McpStdioServerConfig.java | 0 .../copilot/rpc/MemoryConfiguration.java | 0 .../github/copilot/rpc/MessageAttachment.java | 0 .../github/copilot/rpc/MessageOptions.java | 0 .../com/github/copilot/rpc/ModelBilling.java | 0 .../github/copilot/rpc/ModelCapabilities.java | 0 .../rpc/ModelCapabilitiesOverride.java | 0 .../com/github/copilot/rpc/ModelInfo.java | 0 .../com/github/copilot/rpc/ModelLimits.java | 0 .../com/github/copilot/rpc/ModelPolicy.java | 0 .../com/github/copilot/rpc/ModelSupports.java | 0 .../github/copilot/rpc/ModelVisionLimits.java | 0 .../copilot/rpc/NamedProviderConfig.java | 0 .../com/github/copilot/rpc/ParamCoercion.java | 0 .../com/github/copilot/rpc/ParamSchema.java | 0 .../github/copilot/rpc/PermissionHandler.java | 0 .../copilot/rpc/PermissionInvocation.java | 0 .../github/copilot/rpc/PermissionRequest.java | 0 .../copilot/rpc/PermissionRequestResult.java | 0 .../rpc/PermissionRequestResultKind.java | 0 .../com/github/copilot/rpc/PingResponse.java | 0 .../rpc/PostToolUseFailureHandler.java | 0 .../rpc/PostToolUseFailureHookInput.java | 0 .../rpc/PostToolUseFailureHookOutput.java | 0 .../copilot/rpc/PostToolUseHandler.java | 0 .../copilot/rpc/PostToolUseHookInput.java | 0 .../copilot/rpc/PostToolUseHookOutput.java | 0 .../copilot/rpc/PreMcpToolCallHandler.java | 0 .../copilot/rpc/PreMcpToolCallHookInput.java | 0 .../copilot/rpc/PreMcpToolCallHookOutput.java | 0 .../github/copilot/rpc/PreToolUseHandler.java | 0 .../copilot/rpc/PreToolUseHookInput.java | 0 .../copilot/rpc/PreToolUseHookOutput.java | 0 .../github/copilot/rpc/ProviderConfig.java | 0 .../copilot/rpc/ProviderModelConfig.java | 0 .../github/copilot/rpc/ProviderTokenArgs.java | 0 .../copilot/rpc/ResumeSessionConfig.java | 0 .../copilot/rpc/ResumeSessionRequest.java | 0 .../copilot/rpc/ResumeSessionResponse.java | 0 .../github/copilot/rpc/RuntimeConnection.java | 94 ++ .../github/copilot/rpc/SectionOverride.java | 0 .../copilot/rpc/SectionOverrideAction.java | 0 .../copilot/rpc/SendMessageRequest.java | 0 .../copilot/rpc/SendMessageResponse.java | 0 .../copilot/rpc/SessionCapabilities.java | 0 .../com/github/copilot/rpc/SessionConfig.java | 0 .../github/copilot/rpc/SessionContext.java | 0 .../github/copilot/rpc/SessionEndHandler.java | 0 .../copilot/rpc/SessionEndHookInput.java | 0 .../copilot/rpc/SessionEndHookOutput.java | 0 .../com/github/copilot/rpc/SessionHooks.java | 0 .../copilot/rpc/SessionLifecycleEvent.java | 0 .../rpc/SessionLifecycleEventMetadata.java | 0 .../rpc/SessionLifecycleEventTypes.java | 0 .../copilot/rpc/SessionLifecycleHandler.java | 0 .../github/copilot/rpc/SessionListFilter.java | 0 .../github/copilot/rpc/SessionMetadata.java | 0 .../copilot/rpc/SessionStartHandler.java | 0 .../copilot/rpc/SessionStartHookInput.java | 0 .../copilot/rpc/SessionStartHookOutput.java | 0 .../com/github/copilot/rpc/SessionUiApi.java | 0 .../copilot/rpc/SessionUiCapabilities.java | 0 .../rpc/SetForegroundSessionRequest.java | 0 .../rpc/SetForegroundSessionResponse.java | 0 .../copilot/rpc/StdioRuntimeConnection.java | 71 ++ .../copilot/rpc/SystemMessageConfig.java | 0 .../copilot/rpc/SystemMessageSections.java | 0 .../copilot/rpc/SystemPromptSections.java | 0 .../copilot/rpc/TcpRuntimeConnection.java | 116 +++ .../github/copilot/rpc/TelemetryConfig.java | 0 .../github/copilot/rpc/ToolBinaryResult.java | 0 .../com/github/copilot/rpc/ToolDefer.java | 0 .../github/copilot/rpc/ToolDefinition.java | 0 .../com/github/copilot/rpc/ToolHandler.java | 0 .../github/copilot/rpc/ToolInvocation.java | 0 .../github/copilot/rpc/ToolResultObject.java | 0 .../github/copilot/rpc/ToolSearchConfig.java | 0 .../java/com/github/copilot/rpc/ToolSet.java | 0 .../copilot/rpc/UriRuntimeConnection.java | 57 ++ .../github/copilot/rpc/UserInputHandler.java | 0 .../copilot/rpc/UserInputInvocation.java | 0 .../github/copilot/rpc/UserInputRequest.java | 0 .../github/copilot/rpc/UserInputResponse.java | 0 .../rpc/UserPromptSubmittedHandler.java | 0 .../rpc/UserPromptSubmittedHookInput.java | 0 .../rpc/UserPromptSubmittedHookOutput.java | 0 .../rpc/UserPromptTransformedHandler.java | 0 .../rpc/UserPromptTransformedHookInput.java | 0 .../rpc/UserPromptTransformedHookOutput.java | 0 .../com/github/copilot/rpc/package-info.java | 0 .../com/github/copilot/tool/CopilotTool.java | 0 .../tool/CopilotToolMetadataProvider.java | 0 .../github/copilot/tool/CopilotToolParam.java | 0 .../copilot/tool/CopilotToolProcessor.java | 0 .../java/com/github/copilot/tool/Param.java | 0 .../github/copilot/tool/SchemaGenerator.java | 0 java/{ => sdk}/src/main/java/module-info.java | 2 + .../copilot/InternalExecutorProvider.java | 0 .../copilot/ffi/ReaderThreadFactory.java | 15 + .../javax.annotation.processing.Processor | 0 .../main/resources/copilot-runtime.properties | 3 + .../com/github/copilot/AgentInfoTest.java | 0 .../com/github/copilot/AgentModeTest.java | 0 .../java/com/github/copilot/AskUserTest.java | 0 .../ByokBearerTokenProviderE2ETest.java | 0 .../java/com/github/copilot/CapiProxy.java | 23 +- .../copilot/CapiSessionOptionsTest.java | 0 .../github/copilot/CliServerManagerTest.java | 4 +- .../github/copilot/ClientOptionsE2ETest.java | 0 .../copilot/ClosedSessionGuardTest.java | 0 .../java/com/github/copilot/CommandsTest.java | 0 .../com/github/copilot/CompactionTest.java | 0 .../com/github/copilot/ConfigCloneTest.java | 9 + .../github/copilot/CopilotClientModeTest.java | 0 .../com/github/copilot/CopilotClientTest.java | 4 +- .../copilot/CopilotClientTransportTest.java | 387 +++++++++ .../CopilotExperimentalProcessorTest.java | 0 .../CopilotRequestCancelErrorE2ETest.java | 0 .../copilot/CopilotRequestHandlerE2ETest.java | 0 .../CopilotRequestSessionIdE2ETest.java | 0 .../copilot/CopilotRequestTestSupport.java | 3 +- .../github/copilot/CopilotSessionTest.java | 0 .../copilot/CreateSessionReKeyEntryTest.java | 5 +- .../copilot/DataObjectCoverageTest.java | 0 .../copilot/DocumentationSamplesTest.java | 2 +- .../com/github/copilot/E2ETestContext.java | 63 +- .../com/github/copilot/ElicitationTest.java | 0 .../com/github/copilot/ErrorHandlingTest.java | 11 +- .../com/github/copilot/EventFidelityTest.java | 0 .../github/copilot/ExecutorWiringTest.java | 15 +- .../github/copilot/FakeUpstreamServer.java | 5 +- .../copilot/ForwardCompatibilityTest.java | 0 .../copilot/GitHubTelemetryForwardingIT.java | 0 .../github/copilot/GitHubTelemetryTest.java | 33 +- .../java/com/github/copilot/HooksTest.java | 0 .../copilot/InternalExecutorProviderIT.java | 0 .../InternalExecutorProviderProbe.java | 0 .../copilot/InternalExecutorProviderTest.java | 0 .../copilot/JsonIncludeNonNullTest.java | 0 .../com/github/copilot/JsonRpcClientTest.java | 4 +- .../copilot/LifecycleEventManagerTest.java | 0 .../copilot/LowLevelToolDefinitionIT.java | 0 .../github/copilot/ManagedSettingsTest.java | 0 .../com/github/copilot/McpAndAgentsTest.java | 0 .../McpAuthInterestRegistrationTest.java | 0 .../com/github/copilot/McpOAuthE2ETest.java | 0 .../github/copilot/McpOAuthResumeE2ETest.java | 0 .../github/copilot/MessageAttachmentTest.java | 0 .../com/github/copilot/MetadataApiTest.java | 0 .../com/github/copilot/ModeHandlersTest.java | 0 .../com/github/copilot/ModelInfoTest.java | 0 .../github/copilot/ModuleDescriptorTest.java | 0 .../copilot/MultiProviderConfigTest.java | 0 .../copilot/MultiProviderRegistryE2ETest.java | 0 .../copilot/OptionalApiAndJacksonTest.java | 0 .../github/copilot/PerSessionAuthTest.java | 0 .../PermissionRequestResultKindTest.java | 0 .../copilot/PermissionRequestResultTest.java | 0 .../com/github/copilot/PermissionsTest.java | 0 .../copilot/PreMcpToolCallHookTest.java | 0 .../github/copilot/ProviderConfigTest.java | 0 .../copilot/ProviderEndpointE2ETest.java | 0 .../com/github/copilot/RemoteSessionTest.java | 0 .../copilot/RpcHandlerDispatcherTest.java | 0 .../com/github/copilot/RpcServerE2ETest.java | 0 .../github/copilot/RpcServerMiscE2ETest.java | 0 .../copilot/RpcSessionStateExtrasE2ETest.java | 0 .../copilot/RpcTasksAndHandlersE2ETest.java | 0 .../com/github/copilot/RpcWrappersTest.java | 0 .../copilot/SchedulerShutdownRaceTest.java | 0 .../copilot/SessionCanvasSnapshotTest.java | 0 .../github/copilot/SessionConfigE2ETest.java | 0 .../SessionEventDeserializationTest.java | 0 .../copilot/SessionEventHandlingTest.java | 0 .../github/copilot/SessionEventsE2ETest.java | 4 + .../github/copilot/SessionHandlerTest.java | 0 .../copilot/SessionRequestBuilderTest.java | 0 .../copilot/SessionTodosChangedTest.java | 0 .../java/com/github/copilot/SkillsTest.java | 0 .../com/github/copilot/SlashCommandsIT.java | 3 + .../github/copilot/StreamingFidelityTest.java | 46 +- .../github/copilot/SubagentHooksE2ETest.java | 0 .../copilot/SystemMessageSectionsIT.java | 0 .../github/copilot/TelemetryConfigTest.java | 0 .../java/com/github/copilot/TestUtil.java | 0 .../github/copilot/TimeoutEdgeCaseTest.java | 0 .../github/copilot/ToolDefinitionTest.java | 0 .../github/copilot/ToolInvocationTest.java | 0 .../ToolResultObjectSerializationTest.java | 0 .../com/github/copilot/ToolResultsTest.java | 0 .../java/com/github/copilot/ToolSetTest.java | 0 .../java/com/github/copilot/ToolsTest.java | 0 .../UpdateSessionOptionsForModeTest.java | 0 .../copilot/ZeroTimeoutContractTest.java | 0 .../ErgonomicTestTools$$CopilotToolMeta.java | 0 .../copilot/e2e/ErgonomicTestTools.java | 0 .../e2e/ErgonomicToolDefinitionIT.java | 0 .../copilot/e2e/InProcessTransportIT.java | 101 +++ .../github/copilot/e2e/RequireInProcess.java | 64 ++ .../java/com/github/copilot/e2e/RewindIT.java | 0 .../com/github/copilot/e2e/SkipInProcess.java | 64 ++ .../copilot/ffi/FfiRuntimeHostTest.java | 362 ++++++++ .../github/copilot/ffi/InProcessEnvGuard.java | 192 +++++ .../copilot/ffi/JnaNativeBindingTest.java | 344 ++++++++ .../copilot/ffi/NativeRuntimeLoaderTest.java | 580 +++++++++++++ .../copilot/ffi/PlatformDetectorTest.java | 217 +++++ .../copilot/ffi/QueueInputStreamTest.java | 81 ++ .../GeneratedEventTypesCoverageTest.java | 0 .../GeneratedTypesJacksonRoundTripTest.java | 0 .../rpc/GeneratedRpcApiCoverageTest.java | 0 .../rpc/GeneratedRpcRecordsCoverageTest.java | 0 .../github/copilot/rpc/ParamCoercionTest.java | 0 .../github/copilot/rpc/ParamSchemaTest.java | 0 .../copilot/rpc/RecordInvocationArgs.java | 0 .../rpc/ToolDefinitionFromObjectTest.java | 0 .../rpc/ToolDefinitionIsTerminalTest.java | 0 .../copilot/rpc/ToolDefinitionLambdaTest.java | 0 .../ArgCoercionTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/ArgCoercionTools.java | 0 .../DateTimeTools$$CopilotToolMeta.java | 0 .../copilot/rpc/fixtures/DateTimeTools.java | 0 .../DefaultValueTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/DefaultValueTools.java | 0 ...InvocationAwareTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/InvocationAwareTools.java | 0 .../MultiReturnTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/MultiReturnTools.java | 0 .../OptionalParamTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/OptionalParamTools.java | 0 .../OverrideTools$$CopilotToolMeta.java | 0 .../copilot/rpc/fixtures/OverrideTools.java | 0 .../SimpleTools$$CopilotToolMeta.java | 0 .../copilot/rpc/fixtures/SimpleTools.java | 0 ...taticInvocationTools$$CopilotToolMeta.java | 0 .../rpc/fixtures/StaticInvocationTools.java | 0 .../StaticTools$$CopilotToolMeta.java | 0 .../copilot/rpc/fixtures/StaticTools.java | 0 .../tool/CopilotToolAnnotationTest.java | 0 .../tool/CopilotToolProcessorTest.java | 0 .../com/github/copilot/tool/ParamTest.java | 0 .../copilot/tool/SchemaGeneratorTest.java | 0 .../src/test/prompts/PROMPT-smoke-test.md | 0 .../test/resources/logging-debug.properties | 0 .../src/test/resources/logging.properties | 0 scripts/docs-validation/validate.ts | 11 +- 1607 files changed, 7019 insertions(+), 982 deletions(-) create mode 100644 java/copilot-native/pom.xml create mode 100644 java/copilot-native/scripts/fetch-native.mjs create mode 100644 java/copilot-native/scripts/fetch-native.test.mjs create mode 100644 java/copilot-native/src/main/java/.gitkeep create mode 100644 java/copilot-native/src/main/javadoc/.gitkeep create mode 100644 java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties rename java/{ => sdk}/config/checkstyle/checkstyle.xml (100%) rename java/{ => sdk}/config/spotbugs/spotbugs-exclude.xml (100%) rename java/{ => sdk}/jbang-example.java (100%) create mode 100644 java/sdk/pom.xml rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AbortEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AbortReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantIdleEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantIntentEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageDeltaEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageServerTools.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequestType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantReasoningDeltaEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantReasoningEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantStreamingDeltaEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantToolCallDeltaEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantTurnEndEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantTurnRetryEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantTurnStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantUsageApiEndpoint.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsageTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AssistantUsageQuotaSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutoModeResolvedReasoningBucket.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutoModeSwitchCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutoModeSwitchRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutoModeSwitchResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedOperation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/BinaryAssetType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CapabilitiesChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CapabilitiesChangedUI.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CitableSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CitationProvider.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CitationReference.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CitationSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CitationSpan.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/Citations.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CommandCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CommandExecuteEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CommandQueuedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CommandsChangedCommand.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CommandsChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CompactionTrigger.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ContextTier.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/CustomAgentsUpdatedAgent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ElicitationCompletedAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ElicitationCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ElicitationRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ElicitationRequestedMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ElicitationRequestedSchema.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExitPlanModeAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExitPlanModeCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExitPlanModeRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtension.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExternalToolCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ExternalToolRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/FactoryRunUpdatedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HandoffRepository.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HandoffSourceType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HeaderEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HookEndError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HookEndEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HookProgressEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/HookStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMeta.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMetaUI.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedOutcome.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthRequestReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthRequiredStaticClientConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpServerSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpServerStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpServerTransport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallFailureTransport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/PendingMessagesModifiedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/PermissionAllowAllMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/PlanChangedOperation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ReasoningSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SamplingCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SamplingRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ScheduleOrigin.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionAutopilotObjectiveChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionBackgroundTasksChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCompactionStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionContextChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCustomAgentsUpdatedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionCustomNotificationEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionExtensionsAttachmentsPushedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionExtensionsLoadedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionHandoffEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionIdleEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionInfoEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionLimitsConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponseAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionMcpServersLoadedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionModeChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionPlanChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionRemoteSteerableChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionScheduleCancelledEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionScheduleRearmedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionSessionLimitsChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionSkillsLoadedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionSnapshotRewindEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionTaskCompleteEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionTitleChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionTodosChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionToolsUpdatedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionTruncationEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionUsageInfoEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionWarningEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SessionWorkspaceFileChangedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownCodeChanges.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownModelMetric.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownModelMetricRequests.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownModelMetricTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownModelMetricUsage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ShutdownType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SkillInvokedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SkillInvokedTrigger.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SkillSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SubagentDeselectedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SubagentFailedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SubagentSelectedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SystemMessageEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SystemMessageMetadata.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SystemMessageRole.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/SystemNotificationEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/TaskCompletionOutcome.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescription.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMeta.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUI.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUIVisibility.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMeta.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUI.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUICsp.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescription.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMeta.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUI.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUIVisibility.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolSearchActivatedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/ToolUserRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UnknownSessionEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UsageCheckpointModelCacheState.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UserInputCompletedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UserInputRequestedEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UserMessageAgentMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UserMessageDelivery.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/UserMessageEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/Verbosity.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/WorkingDirectoryContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/WorkingDirectoryContextHostType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/WorkspaceFileChangedOperation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/package-info.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountAllUsers.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AccountQuotaSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AdaptiveThinkingSupport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPath.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPathScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentInfoSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryAttentionKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryLastTerminalEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCapture.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCaptureOpenErrorReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnPermissionMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnRegistryTimeout.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnSpawned.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorField.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/BuiltInModelCatalogEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasActionInvokeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasCloseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContextCapabilities.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CanvasSessionContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CapiSessionOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CommandsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadata.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataRepository.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionPathCheck.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ContextHeaviestMessage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ContextTier.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/CurrentToolMetadata.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsCollectedEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntryKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsInclude.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsRedaction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsResultKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSkippedEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredCanvas.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServerType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/EventsAgentScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Extension.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryAbortParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryCurrentPhase.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryDeclaredLimits.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLine.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLineKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseObservation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressLine.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressPage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunConsumed.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunLimits.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/FactoryRunTerminal.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryClientInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryEvent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryNotification.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryCompactContextWindow.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryFileRestoreSkipReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindChangeType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindFilePreview.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindOutcome.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindPoint.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindUnavailableReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HistorySkippedFileRestore.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HookType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstalledPluginInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPath.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathLocation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartTransport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/LocalSessionMetadataValue.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MarketplaceInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MarketplacePluginInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MarketplaceRefreshEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseCapability.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetails.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsAvailableDisplayMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsDisplayMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsPlatform.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsTheme.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsResourceContent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetails.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsAvailableDisplayMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsDisplayMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsPlatform.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsTheme.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpHostState.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpOauthLoginGrantType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpResource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpResourceAnnotations.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpResourceContent.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpResourceIcon.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpResourceTemplate.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpSamplingExecutionAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpServer.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpServerFailureInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpServerNeedsAuthInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpSetEnvValueModeDetails.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/McpTools.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MemoryConfiguration.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotCurrentMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadata.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataRepository.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataTaskType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Model.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelBilling.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelBillingPromo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilities.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimits.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimitsVision.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverride.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimits.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimitsVision.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideSupports.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelPickerCategory.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelPickerPriceCategory.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelPolicy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelPolicyState.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelsGetBuiltInCatalogResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OpenCanvasInstance.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRule.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRuleSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateContextTier.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateEnvValueMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateReasoningSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateToolFilterPrecedence.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PendingPermissionRequest.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionLocationType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionPathsConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionRule.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionRulesSet.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionUrlsConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsAllowAllMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRule.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsModifyRulesScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetAllowAllSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetApproveAllSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodoDependency.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodosRow.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Plugin.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginUpdateAllEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsUninstallParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateAllResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigAzure.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigTransport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigWireApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointTransport.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointWireApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderModelConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderSessionToken.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/QueueInsertMessage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItems.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItemsKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ReasoningSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfigExistingMcSession.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataRepository.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataTaskType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataValue.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionRepository.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RpcCaller.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RpcMapper.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/RunOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimental.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimentalSeatbelt.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyFilesystem.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetwork.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetworkProxy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicySeatbelt.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SendAgentMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SendMessageItem.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SendMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerUserApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAbortParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAbortResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentDeselectParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCancelAllBackgroundAgentsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasCloseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsInvokeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionItem.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionContextHostType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReloadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsSendAttachmentsToMessageParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalPutParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryLogParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsAppendFileParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsErrorCode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsMkdirParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntryType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsRenameParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsRmParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderConventions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionError.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionErrorClass.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionStatement.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionBaselineData.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionClientType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionDetails.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionPredictParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultAvailable.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultUnavailable.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTier.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTierOption.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionUnavailableReason.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLimitsConfig.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionListFilter.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLogLevel.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLogParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLogResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionLspInitializeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsCallToolParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsSetHostContextParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRestartServerParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStartServerParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStopServerParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecordContextChangeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModeGetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicy.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRule.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsEnvValueMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsReasoningSummary.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanDeleteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPlanUpdateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsReloadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueClearParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSetDrainPausedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteNotifySteerableChangedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSendParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSendResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionShutdownParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsDisableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnsureLoadedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionSuspendParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRefreshParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTasksWaitForPendingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetrySetFeatureOverridesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsInitializeAndValidateParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionToolsUpdateSubagentSettingsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContext.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextHostType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesCreateFileParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsCloseParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsForkParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsForkResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetRemoteControlStatusResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsGetSizesResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenAttach.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCloud.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCreate.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoff.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoffTaskType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgress.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStep.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenRemote.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResume.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResumeLast.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenStatus.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsReleaseLockParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsReloadPluginHooksParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsSaveParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsSetAdditionalPluginsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShellInitProfile.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShellInitScript.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShellInitScriptShell.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShellKillSignal.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShellOptions.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ShutdownType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Skill.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryPath.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryScope.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillSource.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SkillsInvokedSkill.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandAgentPromptResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandCompletedResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInfo.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInput.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputChoice.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputCompletion.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInvocationResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandKind.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandOption.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SlashCommandTextResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntry.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntryContextTier.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Tool.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIAutoModeSwitchResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponseAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIElicitationSchema.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingSamplingResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponseAction.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UIUserInputResponse.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsCodeChanges.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricRequests.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricUsage.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsTokenDetail.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/Verbosity.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChangeType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffMode.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspaceSummaryHostType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspacesCheckpoints.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/WorkspacesWorkspaceDetailsHostType.java (100%) rename java/{ => sdk}/src/generated/java/com/github/copilot/generated/rpc/package-info.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/AllowCopilotExperimental.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CliServerManager.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/ConnectionState.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotClient.java (81%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotExperimental.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotExperimentalProcessor.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotRequestContext.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotRequestHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotRequestTransport.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotSession.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotWebSocketCloseStatus.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotWebSocketForwarder.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotWebSocketHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/CopilotWebSocketMessage.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/EventErrorHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/EventErrorPolicy.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/ExtractedTransforms.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/GitHubTelemetryAdapter.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/InternalExecutorProvider.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/JsonRpcClient.java (89%) rename java/{ => sdk}/src/main/java/com/github/copilot/JsonRpcException.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/LifecycleEventManager.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/LlmInferenceAdapter.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/LlmInferenceExchange.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/LlmWebSocketResponseBridge.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/LoggingHelpers.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/RpcHandlerDispatcher.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/SdkProtocolVersion.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/SessionRequestBuilder.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/SystemMessageMode.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/PlatformDetector.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/QueueInputStream.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/ReaderThreadFactory.java create mode 100644 java/sdk/src/main/java/com/github/copilot/ffi/SizeT.java rename java/{ => sdk}/src/main/java/com/github/copilot/package-info.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AgentInfo.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AgentMode.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AgentStopHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AgentStopHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/Attachment.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AutoModeSwitchHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AutoModeSwitchInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AutoModeSwitchRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AutoModeSwitchResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/AzureOptions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/BearerTokenProvider.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/BlobAttachment.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/BuiltInTools.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CapiSessionOptions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CloudSessionOptions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CloudSessionRepository.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CommandContext.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CommandDefinition.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CommandHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CommandWireDefinition.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CopilotClientMode.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java (94%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CopilotExpAssignmentResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/DefaultAgentConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/DeleteSessionResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationContext.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationParams.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationResult.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationResultAction.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ElicitationSchema.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ExitPlanModeHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ExitPlanModeInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ExitPlanModeRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ExitPlanModeResult.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ExpConfigEntry.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetAuthStatusResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetForegroundSessionResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetLastSessionIdResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetMessagesResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetModelsResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetSessionMetadataResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GetStatusResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/HookInvocation.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/rpc/InProcessRuntimeConnection.java rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/InfiniteSessionConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/InputOptions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/JsonRpcError.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/JsonRpcRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/JsonRpcResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/LargeToolOutputConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ListSessionsResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ManagedSettings.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpAuthHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpAuthInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpAuthRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpAuthResult.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpAuthToken.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpHttpServerConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpServerConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/McpStdioServerConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/MemoryConfiguration.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/MessageAttachment.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/MessageOptions.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelBilling.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelCapabilities.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelCapabilitiesOverride.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelInfo.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelLimits.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelPolicy.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelSupports.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ModelVisionLimits.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ParamCoercion.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ParamSchema.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PermissionHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PermissionInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PermissionRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PermissionRequestResultKind.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PingResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseFailureHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PostToolUseHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreMcpToolCallHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreToolUseHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/PreToolUseHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ProviderConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ProviderModelConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ProviderTokenArgs.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/rpc/RuntimeConnection.java rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SectionOverride.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SectionOverrideAction.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SendMessageRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SendMessageResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionCapabilities.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionContext.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionEndHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionEndHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionHooks.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionLifecycleEvent.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionLifecycleEventMetadata.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionLifecycleEventTypes.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionLifecycleHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionListFilter.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionMetadata.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionStartHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionStartHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionUiApi.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SessionUiCapabilities.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SetForegroundSessionRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SetForegroundSessionResponse.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/rpc/StdioRuntimeConnection.java rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SystemMessageSections.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/SystemPromptSections.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/rpc/TcpRuntimeConnection.java rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/TelemetryConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolBinaryResult.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolDefer.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolDefinition.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolResultObject.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolSearchConfig.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/ToolSet.java (100%) create mode 100644 java/sdk/src/main/java/com/github/copilot/rpc/UriRuntimeConnection.java rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserInputHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserInputInvocation.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserInputRequest.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserInputResponse.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/rpc/package-info.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/CopilotTool.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/CopilotToolParam.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/Param.java (100%) rename java/{ => sdk}/src/main/java/com/github/copilot/tool/SchemaGenerator.java (100%) rename java/{ => sdk}/src/main/java/module-info.java (94%) rename java/{ => sdk}/src/main/java25/com/github/copilot/InternalExecutorProvider.java (100%) create mode 100644 java/sdk/src/main/java25/com/github/copilot/ffi/ReaderThreadFactory.java rename java/{ => sdk}/src/main/resources/META-INF/services/javax.annotation.processing.Processor (100%) create mode 100644 java/sdk/src/main/resources/copilot-runtime.properties rename java/{ => sdk}/src/test/java/com/github/copilot/AgentInfoTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/AgentModeTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/AskUserTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ByokBearerTokenProviderE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CapiProxy.java (95%) rename java/{ => sdk}/src/test/java/com/github/copilot/CapiSessionOptionsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CliServerManagerTest.java (98%) rename java/{ => sdk}/src/test/java/com/github/copilot/ClientOptionsE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ClosedSessionGuardTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CommandsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CompactionTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ConfigCloneTest.java (99%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotClientModeTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotClientTest.java (99%) create mode 100644 java/sdk/src/test/java/com/github/copilot/CopilotClientTransportTest.java rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotExperimentalProcessorTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotRequestCancelErrorE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotRequestHandlerE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotRequestSessionIdE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotRequestTestSupport.java (99%) rename java/{ => sdk}/src/test/java/com/github/copilot/CopilotSessionTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java (99%) rename java/{ => sdk}/src/test/java/com/github/copilot/DataObjectCoverageTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/DocumentationSamplesTest.java (99%) rename java/{ => sdk}/src/test/java/com/github/copilot/E2ETestContext.java (90%) rename java/{ => sdk}/src/test/java/com/github/copilot/ElicitationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ErrorHandlingTest.java (94%) rename java/{ => sdk}/src/test/java/com/github/copilot/EventFidelityTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ExecutorWiringTest.java (95%) rename java/{ => sdk}/src/test/java/com/github/copilot/FakeUpstreamServer.java (97%) rename java/{ => sdk}/src/test/java/com/github/copilot/ForwardCompatibilityTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/GitHubTelemetryForwardingIT.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/GitHubTelemetryTest.java (91%) rename java/{ => sdk}/src/test/java/com/github/copilot/HooksTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/InternalExecutorProviderIT.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/InternalExecutorProviderTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/JsonIncludeNonNullTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/JsonRpcClientTest.java (99%) rename java/{ => sdk}/src/test/java/com/github/copilot/LifecycleEventManagerTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/LowLevelToolDefinitionIT.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ManagedSettingsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/McpAndAgentsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/McpAuthInterestRegistrationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/McpOAuthE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/McpOAuthResumeE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/MessageAttachmentTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/MetadataApiTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ModeHandlersTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ModelInfoTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ModuleDescriptorTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/MultiProviderConfigTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/MultiProviderRegistryE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/PerSessionAuthTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/PermissionRequestResultKindTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/PermissionRequestResultTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/PermissionsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ProviderConfigTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ProviderEndpointE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RemoteSessionTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcServerE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcSessionStateExtrasE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcTasksAndHandlersE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/RpcWrappersTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SchedulerShutdownRaceTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionConfigE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionEventDeserializationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionEventHandlingTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionEventsE2ETest.java (98%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionHandlerTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionRequestBuilderTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SessionTodosChangedTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SkillsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SlashCommandsIT.java (98%) rename java/{ => sdk}/src/test/java/com/github/copilot/StreamingFidelityTest.java (86%) rename java/{ => sdk}/src/test/java/com/github/copilot/SubagentHooksE2ETest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/SystemMessageSectionsIT.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/TelemetryConfigTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/TestUtil.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolDefinitionTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolInvocationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolResultObjectSerializationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolResultsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolSetTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ToolsTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/UpdateSessionOptionsForModeTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/ZeroTimeoutContractTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java (100%) create mode 100644 java/sdk/src/test/java/com/github/copilot/e2e/InProcessTransportIT.java create mode 100644 java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java rename java/{ => sdk}/src/test/java/com/github/copilot/e2e/RewindIT.java (100%) create mode 100644 java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/PlatformDetectorTest.java create mode 100644 java/sdk/src/test/java/com/github/copilot/ffi/QueueInputStreamTest.java rename java/{ => sdk}/src/test/java/com/github/copilot/generated/GeneratedEventTypesCoverageTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/ParamCoercionTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/ParamSchemaTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/RecordInvocationArgs.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/ToolDefinitionLambdaTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/rpc/fixtures/StaticTools.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/tool/ParamTest.java (100%) rename java/{ => sdk}/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java (100%) rename java/{ => sdk}/src/test/prompts/PROMPT-smoke-test.md (100%) rename java/{ => sdk}/src/test/resources/logging-debug.properties (100%) rename java/{ => sdk}/src/test/resources/logging.properties (100%) diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 389bcda90..eb77167ce 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,14 +1,14 @@ #!/bin/sh # # Pre-commit hook that runs Spotless check on the Java SDK when Java source -# files are staged. Only triggers if changes exist under java/src/. +# files are staged. Only triggers if changes exist under java/sdk/src/. # # To install this hook, run from the repository root: # git config core.hooksPath .githooks # -# Only run Spotless if staged changes include Java source files under java/src/ -if ! git diff --cached --name-only | grep -q '^java/src/'; then +# Only run Spotless if staged changes include Java source files under java/sdk/src/ +if ! git diff --cached --name-only | grep -q '^java/sdk/src/'; then exit 0 fi diff --git a/.github/actions/java-test-report/action.yml b/.github/actions/java-test-report/action.yml index e826628a0..eedf05372 100644 --- a/.github/actions/java-test-report/action.yml +++ b/.github/actions/java-test-report/action.yml @@ -4,15 +4,15 @@ inputs: report-path: description: "Path to the test report XML files (glob pattern)" required: false - default: "java/target/{surefire-reports*,failsafe-reports}/TEST-*.xml" + default: "java/sdk/target/{surefire-reports*,failsafe-reports}/TEST-*.xml" jacoco-path: description: "Path to the JaCoCo XML report" required: false - default: "java/target/site/jacoco-coverage/jacoco.xml" + default: "java/sdk/target/site/jacoco-coverage/jacoco.xml" jacoco-csv-path: description: "Path to the JaCoCo CSV report" required: false - default: "java/target/site/jacoco-coverage/jacoco.csv" + default: "java/sdk/target/site/jacoco-coverage/jacoco.csv" check-name: description: "Name for the check run" required: false diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 79fa9522c..a9bc22d0e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -11,7 +11,7 @@ - Top-level: `README.md` (architecture + quick start) - Language entry points: `nodejs/src/client.ts`, `python/README.md`, `go/README.md`, `dotnet/README.md` -- Java: `java/README.md`, `java/pom.xml` +- Java: `java/README.md`, `java/pom.xml`, `java/sdk/pom.xml`, `java/copilot-native/pom.xml` - Test harness & E2E: `test/harness/*`, Python harness wrapper `python/e2e/testharness/proxy.py` - Schemas & type generation: `nodejs/scripts/generate-session-types.ts` - Session snapshots used by E2E: `test/snapshots/` (used by the replay proxy) @@ -39,7 +39,7 @@ - E2E runs against a local **replaying CAPI proxy** (see `test/harness/server.ts`). Most language E2E harnesses spawn that server automatically (see `python/e2e/testharness/proxy.py`). - Tests rely on YAML snapshot exchanges under `test/snapshots/` — to add test scenarios, add or edit the appropriate YAML files and update tests. - The harness prints `Listening: http://...` — tests parse this URL to configure CLI or proxy. -- Java E2E tests use `E2ETestContext` which manages a `CapiProxy` (Node.js replaying proxy). The harness is cloned during Maven's `generate-test-resources` phase to `java/target/copilot-sdk/`. +- Java E2E tests use `E2ETestContext` which manages a `CapiProxy` (Node.js replaying proxy). The harness is cloned during Maven's `generate-test-resources` phase to `java/sdk/target/copilot-sdk/`. - Java test method names are converted to lowercase snake_case for snapshot filenames (avoids case collisions on macOS/Windows). ## Project-specific conventions & patterns ✅ @@ -61,13 +61,13 @@ ## Where to add new code or tests 🧭 -- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`, `rust/src`, `java/src/main/java` -- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`, `rust/tests`, `java/src/test/java` -- E2E tests: `*/e2e/` folders that use the shared replay proxy and `test/snapshots/`, `java/src/test/java/**/e2e/` -- Generated types: update schema in `@github/copilot` then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location. Java generated types: `java/src/generated/java` +- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`, `rust/src`, `java/sdk/src/main/java` +- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`, `rust/tests`, `java/sdk/src/test/java` +- E2E tests: `*/e2e/` folders that use the shared replay proxy and `test/snapshots/`, `java/sdk/src/test/java/**/e2e/` +- Generated types: update schema in `@github/copilot` then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location. Java generated types: `java/sdk/src/generated/java` ## Boundaries — files you must NOT hand-edit ⛔ -- `java/src/generated/java/` — auto-generated by `scripts/codegen/java.ts`; regenerate with `cd java && mvn generate-sources -Pcodegen`. +- `java/sdk/src/generated/java/` — auto-generated by `scripts/codegen/java.ts`; regenerate with `cd java && mvn generate-sources -Pcodegen`. - `nodejs/src/generated/` — auto-generated by `npm run generate:session-types`. - `test/snapshots/` — authoritative test fixtures; add/edit YAML here to change E2E behavior, but don't delete without understanding downstream impact. diff --git a/.github/skills/new-java-e2e-test-yaml-and-test/SKILL.md b/.github/skills/new-java-e2e-test-yaml-and-test/SKILL.md index 060228d55..d034b2037 100644 --- a/.github/skills/new-java-e2e-test-yaml-and-test/SKILL.md +++ b/.github/skills/new-java-e2e-test-yaml-and-test/SKILL.md @@ -54,7 +54,7 @@ conversations: ### Step 3: Create the Java IT test class -Place it in `java/src/test/java/com/github/copilot/` with an `IT` suffix +Place it in `java/sdk/src/test/java/com/github/copilot/` with an `IT` suffix (e.g., `MyFeatureIT.java`). The failsafe plugin picks up `*IT.java` files. **Template:** @@ -141,12 +141,12 @@ mvn clean verify | What | Where | |------|-------| -| Test context (manages proxy, workDir, CLI) | `java/src/test/java/com/github/copilot/E2ETestContext.java` | -| Java proxy wrapper | `java/src/test/java/com/github/copilot/CapiProxy.java` | +| Test context (manages proxy, workDir, CLI) | `java/sdk/src/test/java/com/github/copilot/E2ETestContext.java` | +| Java proxy wrapper | `java/sdk/src/test/java/com/github/copilot/CapiProxy.java` | | Replay proxy (TypeScript) | `test/harness/replayingCapiProxy.ts` | | Proxy server entry point | `test/harness/server.ts` | | Snapshot files | `test/snapshots//.yaml` | -| Existing IT tests for reference | `java/src/test/java/com/github/copilot/*IT.java` | +| Existing IT tests for reference | `java/sdk/src/test/java/com/github/copilot/*IT.java` | ## How the Proxy Matches Requests diff --git a/.github/workflows/codegen-check.yml b/.github/workflows/codegen-check.yml index 78927f160..f37a71e45 100644 --- a/.github/workflows/codegen-check.yml +++ b/.github/workflows/codegen-check.yml @@ -15,7 +15,7 @@ on: - 'go/rpc/**' - 'rust/src/generated/**' - 'sdk-protocol-version.json' - - 'java/src/main/java/com/github/copilot/SdkProtocolVersion.java' + - 'java/sdk/src/main/java/com/github/copilot/SdkProtocolVersion.java' - '.github/workflows/codegen-check.yml' workflow_dispatch: @@ -84,7 +84,7 @@ jobs: - name: Verify Java protocol version matches run: | EXPECTED=$(jq -r '.version' sdk-protocol-version.json) - ACTUAL=$(grep -oP 'LATEST\(\K[0-9]+' java/src/main/java/com/github/copilot/SdkProtocolVersion.java) + ACTUAL=$(grep -oP 'LATEST\(\K[0-9]+' java/sdk/src/main/java/com/github/copilot/SdkProtocolVersion.java) if [ "$EXPECTED" != "$ACTUAL" ]; then echo "::error::Java SDK protocol version ($ACTUAL) does not match sdk-protocol-version.json ($EXPECTED). Java manages its own SdkProtocolVersion.java via java/scripts/codegen/. Update it to match." exit 1 diff --git a/.github/workflows/docs-validation.yml b/.github/workflows/docs-validation.yml index 4f53b71e4..dff02f0d2 100644 --- a/.github/workflows/docs-validation.yml +++ b/.github/workflows/docs-validation.yml @@ -9,8 +9,9 @@ on: - 'python/copilot/**' - 'go/**/*.go' - 'dotnet/src/**' - - 'java/src/**' + - 'java/sdk/src/**' - 'java/pom.xml' + - 'java/sdk/pom.xml' - 'scripts/docs-validation/**' - '.github/workflows/docs-validation.yml' workflow_dispatch: diff --git a/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml b/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml index de924a01a..e94e0775c 100644 --- a/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml +++ b/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a5f19a89f89b0693f86ca89ea90e3a633fe19c17bf4d27214fa9124429cdc156","body_hash":"41bc10df4ac9179064417476fbb39fef7423d0998dc0beb7bad42e9eb7ab0494","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a5f19a89f89b0693f86ca89ea90e3a633fe19c17bf4d27214fa9124429cdc156","body_hash":"8db09798070cbcba22c42c50a316ae45c8e8c650eeb23c556b44fde8d519550a","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"8bdba8075360648fe6802302a5b4e016361dc6ac","version":"v0.83.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38","digest":"sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38@sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38","digest":"sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38@sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38","digest":"sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38@sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.3","digest":"sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.3@sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw (v0.83.1). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.md b/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.md index b1623ff78..dd1bfe2bb 100644 --- a/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.md +++ b/.github/workflows/java-adapt-handwritten-code-to-accept-upgrade-changes.md @@ -51,17 +51,17 @@ You are an automation agent that fixes handwritten Java SDK source and test code - The branch `${{ inputs.branch }}` already has: - Updated `java/scripts/codegen/package.json` with the new version - - Regenerated `java/src/generated/java/` code that compiles successfully + - Regenerated `java/sdk/src/generated/java/` code that compiles successfully - Updated the Java POM CLI/version pin property - Your job is ONLY to fix **handwritten** code, NOT generated code. ## Boundaries -- ❌ Do NOT edit anything under `java/src/generated/java/` +- ❌ Do NOT edit anything under `java/sdk/src/generated/java/` - ❌ Do NOT edit `java/scripts/codegen/java.ts` -- ❌ Do NOT create or modify tests in the `com.github.copilot.generated` test package (`java/src/test/java/com/github/copilot/sdk/generated/`) -- ✅ DO edit `java/src/main/java/com/github/copilot/sdk/**` -- ✅ DO edit `java/src/test/java/com/github/copilot/sdk/**` (excluding the `generated` subpackage) +- ❌ Do NOT create or modify tests in the `com.github.copilot.generated` test package (`java/sdk/src/test/java/com/github/copilot/sdk/generated/`) +- ✅ DO edit `java/sdk/src/main/java/com/github/copilot/sdk/**` +- ✅ DO edit `java/sdk/src/test/java/com/github/copilot/sdk/**` (excluding the `generated` subpackage) - ✅ DO add new test methods or test classes if new user-facing API surface is introduced ## Instructions @@ -146,7 +146,7 @@ mvn verify -Dskip.test.harness=true If this passes, commit and push: ```bash -git add java/src/main/java java/src/test/java +git add java/sdk/src/main/java java/sdk/src/test/java git commit -m "Fix handwritten Java code for @github/copilot schema changes Adapt constructor calls, enum references, and test assertions to match diff --git a/.github/workflows/java-codegen-check.yml b/.github/workflows/java-codegen-check.yml index e1c11cd6d..f2f452796 100644 --- a/.github/workflows/java-codegen-check.yml +++ b/.github/workflows/java-codegen-check.yml @@ -6,12 +6,12 @@ on: - main paths: - 'java/scripts/codegen/**' - - 'java/src/generated/**' + - 'java/sdk/src/generated/**' - '.github/workflows/java-codegen-check.yml' pull_request: paths: - 'java/scripts/codegen/**' - - 'java/src/generated/**' + - 'java/sdk/src/generated/**' - '.github/workflows/java-codegen-check.yml' workflow_dispatch: diff --git a/.github/workflows/java-codegen-fix.lock.yml b/.github/workflows/java-codegen-fix.lock.yml index 08005d7f7..1d8d28c43 100644 --- a/.github/workflows/java-codegen-fix.lock.yml +++ b/.github/workflows/java-codegen-fix.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b0390c9ab9beb0d7e106314299e89e486269ab7f64d8489d5021132d79aa6b9b","body_hash":"fe41f8fe1c12cb585cfaefdd755f58d2a84410d9d819cd706a3192ce052e2545","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b0390c9ab9beb0d7e106314299e89e486269ab7f64d8489d5021132d79aa6b9b","body_hash":"63d6ce13a5131b158ddffb10a469aa59e0fdc2278eec4d8de7f6763e0b6f2ea2","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"8bdba8075360648fe6802302a5b4e016361dc6ac","version":"v0.83.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38","digest":"sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38@sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38","digest":"sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38@sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38","digest":"sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38@sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.3","digest":"sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.3@sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}]} # This file was automatically generated by gh-aw (v0.83.1). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/java-codegen-fix.md b/.github/workflows/java-codegen-fix.md index 1fe465f4c..b1dcb1f63 100644 --- a/.github/workflows/java-codegen-fix.md +++ b/.github/workflows/java-codegen-fix.md @@ -54,7 +54,7 @@ You are an automation agent that fixes Java compilation and test failures caused A Dependabot PR bumped the `@github/copilot` npm dependency in `java/scripts/codegen/package.json`. The `java-codegen-check` workflow ran the code generator (`java/scripts/codegen/java.ts`) against the new schemas and `mvn verify` subsequently failed. Your job is to fix **both** the code generator script (if needed) and the handwritten SDK/test source code so the build passes. -**❌❌❌ YOU MUST NEVER EDIT any of the java source code in `java/src/generated/` directly.** ✅✅Rather, the way to affect changes in these files is to change the code generator script and re-generate the classes in `java/src/generated`. +**❌❌❌ YOU MUST NEVER EDIT any of the java source code in `java/sdk/src/generated/` directly.** ✅✅Rather, the way to affect changes in these files is to change the code generator script and re-generate the classes in `java/sdk/src/generated`. The branch to fix is: `${{ inputs.branch }}` The PR number is: `${{ inputs.pr_number }}` @@ -66,7 +66,7 @@ ${{ inputs.error_summary }} ## Architecture overview -The code generator (`java/scripts/codegen/java.ts`) reads JSON schemas from `node_modules/@github/copilot/schemas/` and produces Java source files under `java/src/generated/java/`. These generated types are consumed by handwritten code in `java/src/main/java/` (primarily `CopilotSession.java`) and tested by handwritten tests in `java/src/test/java/`. +The code generator (`java/scripts/codegen/java.ts`) reads JSON schemas from `node_modules/@github/copilot/schemas/` and produces Java source files under `java/sdk/src/generated/java/`. These generated types are consumed by handwritten code in `java/sdk/src/main/java/` (primarily `CopilotSession.java`) and tested by handwritten tests in `java/sdk/src/test/java/`. When `@github/copilot` is bumped, the schemas may change in ways the code generator does not yet handle. Common schema changes include: @@ -126,7 +126,7 @@ Before making fixes, determine whether the failure is caused by: - New schemas exist but no corresponding Java types were generated **(B) Handwritten code referencing old generated type names/shapes.** Signs: -- Compilation errors in `java/src/main/java/` or `java/src/test/java/` referencing types that no longer exist +- Compilation errors in `java/sdk/src/main/java/` or `java/sdk/src/test/java/` referencing types that no longer exist - Test data using old JSON field names Often **both** (A) and (B) apply: the codegen needs fixing first, then handwritten code needs updating. @@ -162,7 +162,7 @@ If the diagnosis shows the code generator does not handle the new schema format: 4. **Verify the generated output** looks reasonable: ```bash - git diff --stat java/src/generated/java/ + git diff --stat java/sdk/src/generated/java/ ``` **You may ONLY modify `java/scripts/codegen/java.ts`.** Do not modify `package.json`, `package-lock.json`, or any other file under `java/scripts/codegen/`. @@ -179,12 +179,12 @@ For each attempt: 2. **Read the generated types** to understand what changed. Check the generated files that the handwritten code references: ```bash # Example: check what a generated type looks like now - cat java/src/generated/java/com/github/copilot/generated/rpc/.java + cat java/sdk/src/generated/java/com/github/copilot/generated/rpc/.java ``` 3. **Fix the affected source files.** You may modify files under: - - `java/src/main/java/` — handwritten SDK source code - - `java/src/test/java/` — handwritten test code + - `java/sdk/src/main/java/` — handwritten SDK source code + - `java/sdk/src/test/java/` — handwritten test code Common fixes: - Update type references from old nested types to new standalone types (e.g. `SessionMcpListResultServersItem` → `McpServer`) @@ -236,12 +236,12 @@ Do **NOT** push broken code. ## Important constraints -- **NEVER** hand-edit files under `java/src/generated/java/` — these are auto-generated. They are updated by running `cd java/scripts/codegen && npx tsx java.ts`. -- **NEVER** modify `java/pom.xml` — build config is not in scope +- **NEVER** hand-edit files under `java/sdk/src/generated/java/` — these are auto-generated. They are updated by running `cd java/scripts/codegen && npx tsx java.ts`. +- **NEVER** modify `java/sdk/pom.xml` — build config is not in scope - **NEVER** modify `java/scripts/codegen/package.json` or `java/scripts/codegen/package-lock.json` — dependency versions are not in scope - **NEVER** modify files under `.github/` — workflow files are not in scope - You **MAY** modify `java/scripts/codegen/java.ts` to fix the code generator -- You **MAY** modify files under `java/src/main/java/` and `java/src/test/java/` to fix handwritten code +- You **MAY** modify files under `java/sdk/src/main/java/` and `java/sdk/src/test/java/` to fix handwritten code - Always run `cd java && mvn spotless:apply` before committing to ensure code formatting - Maximum 3 fix attempts before reporting failure via `noop` - Only push if `mvn verify` passes \ No newline at end of file diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml index f80b38f76..1744a697e 100644 --- a/.github/workflows/java-publish-maven.yml +++ b/.github/workflows/java-publish-maven.yml @@ -173,10 +173,10 @@ jobs: VERSION="${{ steps.versions.outputs.release_version }}" DEV_VERSION="${{ steps.versions.outputs.dev_version }}" ./scripts/test-update-documentation-versions.sh - ./scripts/update-documentation-versions.sh "$VERSION" "$DEV_VERSION" README.md jbang-example.java + ./scripts/update-documentation-versions.sh "$VERSION" "$DEV_VERSION" README.md sdk/jbang-example.java # Commit the documentation changes before release:prepare (requires clean working directory) - git add README.md jbang-example.java + git add README.md sdk/jbang-example.java git commit -m "docs: update version references to ${VERSION}" # Save the commit SHA for potential rollback diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index 20083749e..bd0a34bd2 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -17,6 +17,51 @@ permissions: contents: read jobs: + java-sdk-inprocess: + name: "Java SDK InProcess Tests" + if: github.event.repository.fork == false + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - name: Run Java SDK tests (InProcess) + env: + CI: "true" + run: mvn clean verify -Pinprocess + + - name: Generate Test Report Summary + if: always() + uses: ./.github/actions/java-test-report + with: + title: "Copilot Java SDK :: Test Results InProcess" + + - name: Upload test results on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: java-test-results-inprocess + path: | + java/sdk/target/surefire-reports/ + java/sdk/target/surefire-reports-isolated/ + java/sdk/target/failsafe-reports/ + retention-days: 7 + java-sdk: name: "Java SDK Tests (JDK ${{ matrix.test-jdk }})" if: github.event.repository.fork == false @@ -96,7 +141,7 @@ jobs: run: | echo "Running tests against JDK 25-built classes using JDK 17 runtime..." java -version - mvn jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test-jdk-banner surefire:test failsafe:integration-test failsafe:verify jacoco:report@build-coverage-report-from-tests -Denforcer.skip=true + mvn -pl sdk jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test-jdk-banner surefire:test failsafe:integration-test failsafe:verify jacoco:report@build-coverage-report-from-tests -Denforcer.skip=true - name: Upload test results for site generation if: success() && github.ref == 'refs/heads/main' && matrix.test-jdk == '25' @@ -104,9 +149,9 @@ jobs: with: name: test-results-for-site path: | - java/target/jacoco-test-results/sdk-tests.exec - java/target/surefire-reports/ - java/target/surefire-reports-isolated/ + java/sdk/target/jacoco-test-results/sdk-tests.exec + java/sdk/target/surefire-reports/ + java/sdk/target/surefire-reports-isolated/ retention-days: 1 - name: Generate Test Report Summary @@ -121,7 +166,7 @@ jobs: with: name: java-test-results-jdk-${{ matrix.test-jdk }} path: | - java/target/surefire-reports/ - java/target/surefire-reports-isolated/ - java/target/failsafe-reports/ + java/sdk/target/surefire-reports/ + java/sdk/target/surefire-reports-isolated/ + java/sdk/target/failsafe-reports/ retention-days: 7 diff --git a/.github/workflows/java-smoke-test.yml b/.github/workflows/java-smoke-test.yml index cffef0a35..e7e9a417d 100644 --- a/.github/workflows/java-smoke-test.yml +++ b/.github/workflows/java-smoke-test.yml @@ -63,7 +63,7 @@ jobs: The SDK has already been built and installed into the local Maven repository. JDK 17 and Maven are already installed and on PATH. - Execute the prompt at `src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: + Execute the prompt at `sdk/src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: **Critical override — disable SNAPSHOT updates (but allow downloads):** The goal of this workflow is to validate the SDK SNAPSHOT that was just built and installed locally, not any newer SNAPSHOT that might exist in a remote repository. To ensure Maven does not download a newer timestamped SNAPSHOT of the SDK while still allowing it to download any missing plugins or dependencies, you must run the smoke-test Maven build without `-U` and with `--no-snapshot-updates`, so that it uses the locally installed SDK artifact. Use `mvn --no-snapshot-updates clean package` instead of `mvn -U clean package` or `mvn -o clean package`. @@ -136,7 +136,7 @@ jobs: The SDK has already been built and installed into the local Maven repository. JDK 25 and Maven are already installed and on PATH. - Execute the prompt at `src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: + Execute the prompt at `sdk/src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: **Critical override — disable SNAPSHOT updates (but allow downloads):** The goal of this workflow is to validate the SDK SNAPSHOT that was just built and installed locally, not any newer SNAPSHOT that might exist in a remote repository. To ensure Maven does not download a newer timestamped SNAPSHOT of the SDK while still allowing it to download any missing plugins or dependencies, you must run the smoke-test Maven build without `-U` and with `--no-snapshot-updates`, so that it uses the locally installed SDK artifact. Use `mvn --no-snapshot-updates clean package` instead of `mvn -U clean package` or `mvn -o clean package`. diff --git a/.github/workflows/sdk-consistency-review.lock.yml b/.github/workflows/sdk-consistency-review.lock.yml index 3c1756644..bc33be9ad 100644 --- a/.github/workflows/sdk-consistency-review.lock.yml +++ b/.github/workflows/sdk-consistency-review.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"fb73d13f101fc375308576a64180f63934cc9e8306cb6ef6303f1b9788d9df28","body_hash":"478a74e14ae52b64cd38c57a44953fad4d8003a2414912a19de8f0f4a354a92f","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"fb73d13f101fc375308576a64180f63934cc9e8306cb6ef6303f1b9788d9df28","body_hash":"cc60c817de34cdb662ae4c091203c67a5ef240ca0165b0cd26a842f03b22614f","compiler_version":"v0.83.1","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.73"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"8bdba8075360648fe6802302a5b4e016361dc6ac","version":"v0.83.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38","digest":"sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.38@sha256:cb928eb62d9139a013c2d278dab19af232d35a2d83dca71a3d98eb431f786243"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38","digest":"sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.38@sha256:cd6145620d96acee46e1ede25180a13aa36002467e663db0caa453a8bc8eb60c"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38","digest":"sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.38@sha256:6c19094d95aad5f9f128ad5e583f0f2b894b158aa66c3b86dd9bcc90970a2917"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.3","digest":"sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.3@sha256:3c744710ea275cd5ee65db92a1099e0d980754bd9fafda9ce67704c67004dc83"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.6.0","digest":"sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3","pinned_image":"ghcr.io/github/github-mcp-server:v1.6.0@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"}],"has_pull_request":true} # This file was automatically generated by gh-aw (v0.83.1). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/sdk-consistency-review.md b/.github/workflows/sdk-consistency-review.md index 8a4ff106a..550d9349d 100644 --- a/.github/workflows/sdk-consistency-review.md +++ b/.github/workflows/sdk-consistency-review.md @@ -74,7 +74,7 @@ When a pull request modifies any SDK client code, review it to ensure: - **Python**: `python/copilot/` - **Go**: `go/` - **.NET**: `dotnet/src/` -- **Java**: `java/src/main/java/` +- **Java**: `java/sdk/src/main/java/` - **Rust**: `rust/src/` ## Review Process diff --git a/.github/workflows/update-copilot-dependency.yml b/.github/workflows/update-copilot-dependency.yml index b1a668a7c..9646366ad 100644 --- a/.github/workflows/update-copilot-dependency.yml +++ b/.github/workflows/update-copilot-dependency.yml @@ -192,11 +192,11 @@ jobs: - Enum value additions/renames in generated types - New event types requiring handler registration - Removed or renamed generated types - 3. **Fix handwritten source** (`java/src/main/java/com/github/copilot/sdk/`): + 3. **Fix handwritten source** (`java/sdk/src/main/java/com/github/copilot/sdk/`): - Update call sites passing positional constructor args to include new fields (typically `null` for optional new fields). - Update switch/if-else over enum values to handle new cases. - Register handlers for new event types in `CopilotSession.java` if applicable. - 4. **Fix handwritten tests** (`java/src/test/java/com/github/copilot/sdk/`): + 4. **Fix handwritten tests** (`java/sdk/src/test/java/com/github/copilot/sdk/`): - Same constructor/enum fixes as above. - Add new test methods for new functionality if the change adds user-facing API surface. 5. **Validate**: `cd java && mvn clean test-compile jar:jar && mvn verify -Dskip.test.harness=true` diff --git a/.gitignore b/.gitignore index 4aff9be11..c1e983376 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,10 @@ docs/.validation/ *.csproj.lscache # Java -java/target +java/**/target/ java/smoke-test java/.classpath java/.project java/.settings java/scripts/codegen/node_modules/ +.flattened-pom.xml diff --git a/java/README.md b/java/README.md index 8001d87d8..ea72b72b1 100644 --- a/java/README.md +++ b/java/README.md @@ -70,6 +70,52 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. implementation 'com.github:copilot-sdk-java:1.0.11-preview.0-SNAPSHOT' ``` +## In-process mode (experimental) + +The SDK supports running the Copilot runtime **in-process** as a native library instead of spawning a separate CLI process. This eliminates process management overhead and simplifies deployment. In-process mode is currently experimental and only supported on **linux-x64**. + +Because in-process mode is experimental, see the [Using experimental APIs](#using-experimental-apis) section for how to opt in. + +### Additional dependency + +Add both the SDK and the platform-specific native runtime to your project: + +```xml + + + + com.github + copilot-sdk-java + ${copilot.version} + + + + com.github + copilot-sdk-java-runtime + ${copilot.version} + linux-x64 + + + + net.java.dev.jna + jna + 5.19.1 + + +``` + +### Usage + +Configure the client to use the in-process connection: + +```java +CopilotClientOptions options = new CopilotClientOptions() + .setConnection(RuntimeConnection.forInProcess()); + +CopilotClient client = new CopilotClient(options); +client.start().get(); +``` + ## Quick Start ```java @@ -156,12 +202,12 @@ PermissionHandler handler = (request, invocation) -> { You can run the SDK without setting up a full Java project, by using [JBang](https://www.jbang.dev/). -See the full source of [`jbang-example.java`](jbang-example.java) for a complete example with more features like session idle handling and usage info events. +See the full source of [`jbang-example.java`](sdk/jbang-example.java) for a complete example with more features like session idle handling and usage info events. Or run it directly from the repository: ```bash -jbang https://github.com/github/copilot-sdk/blob/main/java/jbang-example.java +jbang https://github.com/github/copilot-sdk/blob/main/java/sdk/jbang-example.java ``` ## Annotation-based tools and `ToolInvocation` context @@ -443,4 +489,4 @@ mvn jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test- ## License -MIT — see [LICENSE](LICENSE) for details. +MIT — see [LICENSE](sdk/LICENSE) for details. diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml new file mode 100644 index 000000000..a6e3869b8 --- /dev/null +++ b/java/copilot-native/pom.xml @@ -0,0 +1,269 @@ + + + + 4.0.0 + + + com.github + copilot-sdk-java-parent + 1.0.11-preview.0-SNAPSHOT + ../pom.xml + + + com.github + copilot-sdk-java-runtime + jar + + GitHub Copilot SDK :: Java :: Native Runtime + Native runtime binaries for the GitHub Copilot Java SDK, published as per-platform classifier JARs + https://github.com/github/copilot-sdk + + + scm:git:https://github.com/github/copilot-sdk.git + scm:git:https://github.com/github/copilot-sdk.git + https://github.com/github/copilot-sdk + HEAD + + + + + ${project.basedir}/../.. + + linux-x64 + ${project.build.directory}/native-staging + + false + + + + + + + src/main/resources + true + + + + + + org.codehaus.mojo + exec-maven-plugin + + + fetch-native-linux-x64 + generate-resources + + exec + + + node + + ${project.basedir}/scripts/fetch-native.mjs + ${copilot.sdk.root} + ${copilot.native.staging} + ${copilot.native.classifier} + + + + + test-fetch-native + test + + exec + + + node + + --test + ${project.basedir}/scripts/fetch-native.test.mjs + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + jar-linux-x64 + package + + jar + + + ${copilot.native.classifier} + ${copilot.native.staging}/${copilot.native.classifier} + + .version + + + + + + empty-javadoc-jar + package + + jar + + + javadoc + ${project.basedir}/src/main/javadoc + + + + + empty-sources-jar + package + + jar + + + sources + ${project.basedir}/src/main/java + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.sonatype.central + central-publishing-maven-plugin + true + + central + true + + + + + + + + + skip-native-download + + + copilot.native.skip.download + true + + + + + + org.codehaus.mojo + exec-maven-plugin + + true + + + + org.apache.maven.plugins + maven-jar-plugin + + + jar-linux-x64 + none + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + none + + + + + + + + diff --git a/java/copilot-native/scripts/fetch-native.mjs b/java/copilot-native/scripts/fetch-native.mjs new file mode 100644 index 000000000..7b68f0406 --- /dev/null +++ b/java/copilot-native/scripts/fetch-native.mjs @@ -0,0 +1,134 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +/** + * Downloads the `runtime.node` native binary for a single platform classifier + * and stages it for packaging into a classifier JAR. + * + * Steps: + * 1. Read the pinned version and the SHA-512 `integrity` value for + * `@github/copilot-` from `nodejs/package-lock.json`. + * 2. `npm pack` that exact version into the staging directory. + * 3. Verify the downloaded tarball against the `integrity` value. + * 4. Extract `package/prebuilds//runtime.node` to + * `//native//runtime.node`. + * 5. Extract `package/copilot` (or `package/copilot.exe` on Windows) to + * `//native//copilot`. + * 6. Write `//native//platform.properties`. + * + * Usage: node fetch-native.mjs + */ + +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const [repoRoot, stagingDir, classifier] = process.argv.slice(2); + +if (!repoRoot || !stagingDir || !classifier) { + console.error('Usage: node fetch-native.mjs '); + process.exit(1); +} + +const lockPath = path.join(repoRoot, 'nodejs', 'package-lock.json'); +const packageName = `@github/copilot-${classifier}`; +const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); +const entry = lock.packages?.[`node_modules/${packageName}`]; + +if (!entry?.version || !entry?.integrity) { + console.error(`Could not find version/integrity for ${packageName} in ${lockPath}`); + process.exit(1); +} + +const { version, integrity } = entry; +if (!integrity.startsWith('sha512-')) { + console.error(`Unsupported integrity algorithm for ${packageName}: ${integrity}`); + process.exit(1); +} + +const outDir = path.join(stagingDir, classifier); +const resourceDir = path.join(outDir, 'native', classifier); +const runtimePath = path.join(resourceDir, 'runtime.node'); +const isWindows = classifier.startsWith('win32'); +const cliTarballMember = isWindows ? 'package/copilot.exe' : 'package/copilot'; +const cliFilename = isWindows ? 'copilot.exe' : 'copilot'; +const cliPath = path.join(resourceDir, cliFilename); +const platformPropertiesPath = path.join(resourceDir, 'platform.properties'); +const expectedPlatformProperties = `classifier=${classifier}\nversion=${version}\n`; +const stampPath = path.join(outDir, '.version'); + +// Idempotence: skip the download only when every required staged artifact +// matches the package identity recorded in the stamp. +if ( + fs.existsSync(runtimePath) && + fs.existsSync(cliPath) && + fs.existsSync(platformPropertiesPath) && + fs.existsSync(stampPath) +) { + const stampLines = fs.readFileSync(stampPath, 'utf8').trim().split('\n'); + const stampVersion = stampLines[0] || ''; + const stampIntegrity = stampLines[1] || ''; + const stampRuntimeDigest = stampLines[2] || ''; + const stampCliDigest = stampLines[3] || ''; + const currentRuntimeDigest = digestFile(runtimePath); + const currentCliDigest = digestFile(cliPath); + const currentPlatformProperties = fs.readFileSync(platformPropertiesPath, 'utf8'); + if ( + stampVersion === version && + stampIntegrity === integrity && + stampRuntimeDigest === currentRuntimeDigest && + stampCliDigest === currentCliDigest && + currentPlatformProperties === expectedPlatformProperties + ) { + console.log(`${packageName}@${version} already staged at ${runtimePath}`); + process.exit(0); + } +} + +fs.rmSync(outDir, { recursive: true, force: true }); +fs.mkdirSync(resourceDir, { recursive: true }); + +console.log(`Downloading ${packageName}@${version} ...`); +const packOutput = execFileSync('npm', ['pack', `${packageName}@${version}`, '--pack-destination', outDir], { + encoding: 'utf8', + shell: process.platform === 'win32', +}); +const tarballName = packOutput.trim().split('\n').pop().trim(); +const tarballPath = path.join(outDir, tarballName); + +const actual = `sha512-${createHash('sha512').update(fs.readFileSync(tarballPath)).digest('base64')}`; +if (actual !== integrity) { + console.error(`Integrity verification failed for ${tarballPath}`); + console.error(` expected: ${integrity}`); + console.error(` actual: ${actual}`); + process.exit(1); +} +console.log(`Integrity verified (${integrity.slice(0, 20)}...).`); + +const memberPath = `package/prebuilds/${classifier}/runtime.node`; +execFileSync('tar', ['-xzf', tarballPath, '-C', outDir, memberPath], { stdio: 'inherit' }); +fs.renameSync(path.join(outDir, memberPath), runtimePath); + +// Extract the copilot CLI executable (necessary-and-sufficient runtime artifact invariant: +// host_start needs both runtime.node and the copilot CLI from the same package version). +execFileSync('tar', ['-xzf', tarballPath, '-C', outDir, cliTarballMember], { stdio: 'inherit' }); +fs.renameSync(path.join(outDir, cliTarballMember), cliPath); +if (!isWindows) { + fs.chmodSync(cliPath, 0o755); +} + +fs.rmSync(path.join(outDir, 'package'), { recursive: true, force: true }); +fs.rmSync(tarballPath, { force: true }); + +fs.writeFileSync(platformPropertiesPath, expectedPlatformProperties); +const runtimeDigest = digestFile(runtimePath); +const cliDigest = digestFile(cliPath); +fs.writeFileSync(stampPath, `${version}\n${integrity}\n${runtimeDigest}\n${cliDigest}\n`); + +console.log(`Staged ${runtimePath}`); + +function digestFile(filePath) { + return `sha512-${createHash('sha512').update(fs.readFileSync(filePath)).digest('base64')}`; +} diff --git a/java/copilot-native/scripts/fetch-native.test.mjs b/java/copilot-native/scripts/fetch-native.test.mjs new file mode 100644 index 000000000..a80cd0387 --- /dev/null +++ b/java/copilot-native/scripts/fetch-native.test.mjs @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import test from 'node:test'; + +const classifier = 'linux-x64'; +const version = '1.0.79'; +const integrity = 'sha512-test-integrity'; +const runtimeContent = 'runtime content'; +const cliContent = 'cli content'; +const scriptPath = fileURLToPath(new URL('./fetch-native.mjs', import.meta.url)); + +test('missing CLI does not use incremental fast path', (t) => { + const fixture = createFixture(t); + fs.rmSync(fixture.cliPath); + + const result = runScript(fixture); + + assertRestagingAttempted(fixture, result); +}); + +test('stale CLI does not use incremental fast path', (t) => { + const fixture = createFixture(t); + fs.writeFileSync(fixture.cliPath, 'stale CLI content'); + + const result = runScript(fixture); + + assertRestagingAttempted(fixture, result); +}); + +test('missing platform metadata does not use incremental fast path', (t) => { + const fixture = createFixture(t); + fs.rmSync(fixture.platformPropertiesPath); + + const result = runScript(fixture); + + assertRestagingAttempted(fixture, result); +}); + +test('complete matching artifacts use incremental fast path', (t) => { + const fixture = createFixture(t); + + const result = runScript(fixture); + + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /already staged/); + assert.equal(fs.existsSync(fixture.npmMarkerPath), false); +}); + +function createFixture(t) { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'fetch-native-test-')); + t.after(() => fs.rmSync(root, { recursive: true, force: true })); + + const repoRoot = path.join(root, 'repo'); + const stagingDir = path.join(root, 'staging'); + const resourceDir = path.join(stagingDir, classifier, 'native', classifier); + const fakeBinDir = path.join(root, 'bin'); + const npmMarkerPath = path.join(root, 'npm-invoked'); + fs.mkdirSync(path.join(repoRoot, 'nodejs'), { recursive: true }); + fs.mkdirSync(resourceDir, { recursive: true }); + fs.mkdirSync(fakeBinDir); + + fs.writeFileSync( + path.join(repoRoot, 'nodejs', 'package-lock.json'), + JSON.stringify({ + packages: { + [`node_modules/@github/copilot-${classifier}`]: { version, integrity }, + }, + }), + ); + + const runtimePath = path.join(resourceDir, 'runtime.node'); + const cliPath = path.join(resourceDir, 'copilot'); + const platformPropertiesPath = path.join(resourceDir, 'platform.properties'); + fs.writeFileSync(runtimePath, runtimeContent); + fs.writeFileSync(cliPath, cliContent); + fs.writeFileSync(platformPropertiesPath, `classifier=${classifier}\nversion=${version}\n`); + fs.writeFileSync( + path.join(stagingDir, classifier, '.version'), + `${version}\n${integrity}\n${digest(runtimeContent)}\n${digest(cliContent)}\n`, + ); + + const fakeNpmPath = path.join(fakeBinDir, 'npm'); + fs.writeFileSync(fakeNpmPath, '#!/bin/sh\nprintf invoked > \"$FETCH_NATIVE_NPM_MARKER\"\nexit 42\n'); + fs.chmodSync(fakeNpmPath, 0o755); + + return { + repoRoot, + stagingDir, + fakeBinDir, + npmMarkerPath, + runtimePath, + cliPath, + platformPropertiesPath, + }; +} + +function runScript(fixture) { + return spawnSync(process.execPath, [scriptPath, fixture.repoRoot, fixture.stagingDir, classifier], { + encoding: 'utf8', + env: { + ...process.env, + PATH: `${fixture.fakeBinDir}${path.delimiter}${process.env.PATH}`, + FETCH_NATIVE_NPM_MARKER: fixture.npmMarkerPath, + }, + }); +} + +function assertRestagingAttempted(fixture, result) { + assert.notEqual(result.status, 0, 'The fake npm command should make restaging fail'); + assert.equal(fs.readFileSync(fixture.npmMarkerPath, 'utf8'), 'invoked'); +} + +function digest(content) { + return `sha512-${createHash('sha512').update(content).digest('base64')}`; +} diff --git a/java/copilot-native/src/main/java/.gitkeep b/java/copilot-native/src/main/java/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/java/copilot-native/src/main/javadoc/.gitkeep b/java/copilot-native/src/main/javadoc/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties new file mode 100644 index 000000000..0f3230898 --- /dev/null +++ b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties @@ -0,0 +1,12 @@ +# Placeholder marker for the primary (classifier-less) artifact of +# com.github:copilot-sdk-java-runtime. +# +# The real native binaries ship in per-platform classifier JARs +# (e.g. copilot-sdk-java-runtime--linux-x64.jar) under +# native//runtime.node. This primary JAR exists only to satisfy +# Maven Central's requirement for a main artifact and intentionally contains +# no native binaries. +# +# This file is processed by Maven resource filtering. +placeholder=true +version=${project.version} diff --git a/java/docs/adr/adr-007-native-bundling-strategy.md b/java/docs/adr/adr-007-native-bundling-strategy.md index 1322e199e..f561842fe 100644 --- a/java/docs/adr/adr-007-native-bundling-strategy.md +++ b/java/docs/adr/adr-007-native-bundling-strategy.md @@ -1,85 +1,109 @@ -# ADR-007: Native runtime bundling strategy — per-platform classifier JARs +# ADR-007: Native runtime bundling strategy: per-platform classifier JARs -## Context and Problem Statement +## Context and problem statement -The Copilot SDK for Java currently has no embedded runtime. It depends on an externally provided runtime process (see epic [#1917](https://github.com/github/copilot-sdk/issues/1917)). The ongoing Rust port of the `copilot-agent-runtime` repository is reaching the point where the runtime can be consumed as a native shared library without requiring a Node.js process, making it practical to embed the runtime directly in the SDK JAR. +The Copilot SDK for Java supports an experimental in-process connection that loads the Copilot agent runtime as a native shared library. The existing stdio, TCP, and URI connections remain the default behavior unless the user explicitly selects the in-process connection. ### The runtime artifact The artifact to be embedded is `runtime.node`, a Rust [`cdylib`](#references) produced by the `src/runtime` crate in `github/copilot-agent-runtime` using the [napi-rs](#references) build toolchain. Despite the `.node` file extension (a naming convention of napi-rs), this is an ordinary platform-specific shared library (`.so` on Linux, `.dylib` on macOS, `.dll` on Windows). It exposes two front doors built over the same internal engine: -- **[napi](#references) front door** — loaded by a Node.js process as a native addon (current CLI path). -- **[C ABI](#references) front door** — a fixed set of approximately 12 `extern "C"` lifecycle and transport entry points (`copilot_runtime_server_create`, `copilot_runtime_connection_open`, etc.) that any language can call in-process via [FFI](#references) ([JNA](#references) for Java, Python/cffi, C#/`DllImport`, Go/purego) **without a Node.js process**. All API methods travel as JSON-RPC data through this fixed transport; the export list never changes as the method set grows. +* **[napi](#references) front door**: loaded by a Node.js process as a native addon for the current CLI path. +* **[C ABI](#references) front door**: a fixed set of 5 `extern "C"` lifecycle and transport entry points that any language can call in-process via [FFI](#references) ([JNA](#references) for Java, Python/cffi, C#/`DllImport`, Go/purego). All API methods travel as JSON-RPC data through this fixed transport; the export list does not change as the method set grows. + + | Entry point | C signature | Purpose | + | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `copilot_runtime_host_start` | `(const uint8_t* argv_json, size_t argv_json_len, const uint8_t* env_json, size_t env_json_len) → uint32_t` | Start the runtime host; `argv_json` is a JSON array (e.g., `["copilot","--embedded-host"]`), `env_json` is an optional JSON object of environment overrides. Returns a server handle (0 = failure). | + | `copilot_runtime_host_shutdown` | `(uint32_t server_id) → bool` | Shut down the runtime host identified by `server_id`. | + | `copilot_runtime_connection_open` | `(uint32_t server_id, void(*on_outbound)(void* user_data, const uint8_t* data, size_t len), void* user_data, const uint8_t* ext_source, size_t ext_source_len, const uint8_t* ext_name, size_t ext_name_len, const uint8_t* conn_token, size_t conn_token_len) → uint32_t` | Open a bidirectional connection on the server; registers the `on_outbound` callback for runtime→SDK data delivery. `ext_source`, `ext_name`, and `conn_token` are nullable metadata buffers. Returns a connection handle (0 = failure). | + | `copilot_runtime_connection_write` | `(uint32_t connection_id, const uint8_t* data, size_t len) → bool` | Write a JSON-RPC frame from the SDK into the runtime. The native side copies the buffer synchronously before returning. | + | `copilot_runtime_connection_close` | `(uint32_t connection_id) → bool` | Close a connection. | + + The outbound callback signature: `void on_outbound(void* user_data, const uint8_t* data, size_t len)` — invoked by native code (potentially on native threads) to deliver JSON-RPC responses and notifications back to the SDK. The `cli-native.node` addon — a separate, smaller artifact that provides ICU4X text segmentation, Win32 API wrappers, and terminal UI helpers — is a CLI-only artifact used by the Ink/React terminal interface. It is **not needed** by the Java SDK. ### Note on the active Rust migration -As of 2026-07, the `runtime.node` binary is being built up iteratively as TypeScript runtime code is ported into it. It is **not** being reduced; it is growing with each port PR. The `embedded_host.rs` module in the runtime currently spawns a short-lived child process to service method bodies not yet ported to Rust. This internal Node.js dependency shrinks with each port PR and is expected to disappear entirely when the migration completes. The C ABI surface and loading mechanism described in this ADR are stable regardless of migration progress. +As of 2026-08, the `runtime.node` binary is being built up iteratively as TypeScript runtime code is ported into it. It is **not** being reduced; it is growing with each port PR. The `embedded_host.rs` module currently starts a child `copilot --embedded-host` process to service method bodies not yet ported to Rust. + +The classifier JAR therefore contains a version-matched pair during the migration: + +* `runtime.node`: loaded into the Java process through JNA +* `copilot` or `copilot.exe`: started internally by `copilot_runtime_host_start` +* `platform.properties`: classifier and runtime version metadata + +The Java SDK does not independently spawn this child for JSON-RPC transport. The native runtime owns the transitional embedded-host process. The bundled CLI requirement disappears after the Rust migration is complete, while the C ABI and Java loading mechanism remain stable. ### Platform dimensions The runtime must be built for each unique combination of OS, CPU architecture, and (on Linux) C runtime variant. The build system in `github/copilot-agent-runtime` produces eight Rust target triples: -| Platform label | Rust triple | Constraint | -|---------------|-------------|------------| -| `linux-x64` | `x86_64-unknown-linux-gnu` | [glibc](#references) ≥ 2.28 (Debian 10+, Ubuntu 20.04+, RHEL 8+) | -| `linux-arm64` | `aarch64-unknown-linux-gnu` | glibc ≥ 2.28 | -| `linuxmusl-x64` | `x86_64-unknown-linux-musl` | dynamically links [musl libc](#references) (Alpine Linux) | -| `linuxmusl-arm64` | `aarch64-unknown-linux-musl` | dynamically links musl libc | -| `darwin-x64` | `x86_64-apple-darwin` | macOS, Intel | -| `darwin-arm64` | `aarch64-apple-darwin` | macOS, Apple Silicon | -| `win32-x64` | `x86_64-pc-windows-msvc` | [MSVC CRT](#references) statically linked (`+crt-static`) | -| `win32-arm64` | `aarch64-pc-windows-msvc` | MSVC CRT statically linked (`+crt-static`) | +| Platform label | Rust triple | Constraint | +| ----------------- | ---------------------------- | ---------------------------------------------------------------- | +| `linux-x64` | `x86_64-unknown-linux-gnu` | [glibc](#references) ≥ 2.28 (Debian 10+, Ubuntu 20.04+, RHEL 8+) | +| `linux-arm64` | `aarch64-unknown-linux-gnu` | glibc ≥ 2.28 | +| `linuxmusl-x64` | `x86_64-unknown-linux-musl` | dynamically links [musl libc](#references) (Alpine Linux) | +| `linuxmusl-arm64` | `aarch64-unknown-linux-musl` | dynamically links musl libc | +| `darwin-x64` | `x86_64-apple-darwin` | macOS, Intel | +| `darwin-arm64` | `aarch64-apple-darwin` | macOS, Apple Silicon | +| `win32-x64` | `x86_64-pc-windows-msvc` | [MSVC CRT](#references) statically linked (`+crt-static`) | +| `win32-arm64` | `aarch64-pc-windows-msvc` | MSVC CRT statically linked (`+crt-static`) | The GNU/Linux glibc minimum of 2.28 is enforced at build time via a Microsoft/vscode-linux-build-agent sysroot and verified post-build by `script/linux/verify-glibc-requirements.sh`. The musl binaries are **not** fully statically linked; they dynamically link musl libc (`-C target-feature=-crt-static` is explicitly set at build time). The **common case** (Windows × 2 + macOS × 2 + GNU/Linux × 2) requires **6 binaries**. Supporting Alpine Linux adds 2 more musl binaries for a total of **8**. -### Platform selection is 100% deterministic +### Platform selection -The correct binary can be selected at runtime without any heuristics, using only standard Java and OS APIs: +The loader selects a classifier at runtime using standard Java and OS APIs: -1. **OS**: `System.getProperty("os.name")` — distinguishes Windows, macOS, and Linux unambiguously. -2. **Architecture**: `System.getProperty("os.arch")` — `"amd64"` and `"x86_64"` both map to `x64`; `"aarch64"` and `"arm64"` both map to `arm64`. -3. **Linux libc variant**: Read the first 2 KB of `/proc/self/exe` and parse the [ELF](#references) PT_INTERP segment (the dynamic linker path). If the interpreter path contains `/ld-musl-` → musl; if it contains `/ld-linux-` → glibc. This requires no subprocess, no PATH lookup, and works inside containers. This is the same approach used by the `detect-libc` npm package (its primary, most reliable detection method). +1. **OS**: `System.getProperty("os.name")` distinguishes Windows, macOS, and Linux. +1. **Architecture**: `System.getProperty("os.arch")` maps `"amd64"`, `"x86_64"`, and `"x64"` to `x64`, and maps `"aarch64"` and `"arm64"` to `arm64`. +1. **Linux libc variant**: The loader reads the first 2 KB of `/proc/self/exe` and parses the [ELF](#references) PT_INTERP segment. An interpreter containing `/ld-musl-` selects musl, while `/ld-linux-` selects glibc. + +If the Linux executable cannot be read or its interpreter is not recognized, the implementation falls back to the GNU/Linux classifier for the detected architecture. Unsupported operating systems and architectures fail with `IllegalStateException`. ### Size baseline Measured from `github/copilot-agent-runtime` release `cli-1.0.69-2` (2026-07-06): -| Platform | `runtime.node` (uncompressed) | Compressed (~40% deflate) | -|----------|------------------------------|--------------------------| -| `linux-x64` | 64.7 MB | ~25.9 MB | -| `linux-arm64` | 55.5 MB | ~22.2 MB | -| `linuxmusl-x64` | 64.4 MB | ~25.8 MB | -| `linuxmusl-arm64` | 55.3 MB | ~22.1 MB | -| `darwin-x64` | 57.3 MB | ~22.9 MB | -| `darwin-arm64` | 48.1 MB | ~19.2 MB | -| `win32-x64` | 55.9 MB | ~22.4 MB | -| `win32-arm64` | 48.4 MB | ~19.4 MB | +| Platform | `runtime.node` (uncompressed) | Compressed (~40% deflate) | +| ----------------- | ----------------------------- | ------------------------- | +| `linux-x64` | 64.7 MB | ~25.9 MB | +| `linux-arm64` | 55.5 MB | ~22.2 MB | +| `linuxmusl-x64` | 64.4 MB | ~25.8 MB | +| `linuxmusl-arm64` | 55.3 MB | ~22.1 MB | +| `darwin-x64` | 57.3 MB | ~22.9 MB | +| `darwin-arm64` | 48.1 MB | ~19.2 MB | +| `win32-x64` | 55.9 MB | ~22.4 MB | +| `win32-arm64` | 48.4 MB | ~19.4 MB | + +The published Java SDK JAR (`copilot-sdk-java-1.0.6-preview.1.jar`) is currently **1.53 MB**. A future runtime-only monolithic JAR containing all 6 common-case native binaries would be approximately **132 MB** compressed; all 8 including musl would be approximately **180 MB** compressed. -The published Java SDK JAR (`copilot-sdk-java-1.0.6-preview.1.jar`) is currently **1.53 MB**. A monolithic JAR containing all 6 common-case native binaries would be approximately **132 MB** compressed; all 8 including musl would be approximately **180 MB** compressed. +These runtime-only estimates do not describe the current migration artifact. The current development `linux-x64` classifier JAR also contains the version-matched CLI executable and is approximately **152 MB compressed**. Its staged contents are approximately **133 MB** for `runtime.node` and **170 MB** for `copilot` before JAR compression. All native dependencies within the runtime (`rustls`/`aws-lc-rs` for TLS, `rusqlite` with `bundled` feature for SQLite, `zlib-rs` for compression) are statically compiled into the binary. There are no dependencies on system OpenSSL, libgit2, or libz. -## Considered Options +## Considered options -### Option 1: Monolithic JAR — all platform binaries in one artifact +### Option 1: Monolithic JAR with all platform binaries -All 6 (or 8) `runtime.node` binaries are bundled inside the single `copilot-sdk-java` artifact. At runtime the SDK extracts and loads the one matching the current platform; the remaining 5–7 are carried silently. +All 6 (or 8) platform artifact sets are bundled inside a single monolithic artifact. At runtime the SDK extracts and loads the one matching the current platform; the remaining 5–7 are carried silently. **Advantages:** + - Single `` in `pom.xml`; zero extra configuration for users. - Familiar pattern: [ONNX Runtime](#references) (`onnxruntime-1.21.0.jar`, **130 MB**, all platforms) demonstrates this is an accepted norm in the Java ML ecosystem. **Drawbacks:** + - Every user downloads every platform regardless of their target. A developer on Apple Silicon downloads 105+ MB of Linux and Windows binaries they will never use. - Build tooling (thin Docker layers, incremental CI caches, artifact registries) penalises large JARs. A single 132–180 MB JAR invalidates the entire cache whenever any platform's binary changes. - Maven's dependency resolution has no mechanism to supply platform-appropriate variants automatically; platform selection must happen entirely at runtime inside the JAR. - Conflicts with the principle that Maven artifacts should be reproducible and minimal. -### Option 2: Per-platform classifier JARs ([DJL](#references) style) +### Option 2: Per-platform classifier JARs A small, pure-Java coordination artifact (`copilot-sdk-java`, ~1.5 MB) is published alongside separate per-platform native artifacts differentiated by Maven classifier: @@ -94,7 +118,7 @@ com.github:copilot-sdk-java-runtime:VERSION:win32-x64 com.github:copilot-sdk-java-runtime:VERSION:win32-arm64 ``` -Each classifier JAR contains only the `runtime.node` binary for that platform (~19–26 MB compressed) plus a small `.properties` metadata file. The coordination artifact selects and loads the matching native at startup. +Each classifier JAR contains `runtime.node`, `platform.properties`, and, during the active Rust migration, the version-matched `copilot` or `copilot.exe` embedded-host executable. The coordination artifact selects and loads the matching native when the user selects the in-process connection. This is the same pattern used by DJL's PyTorch native artifacts (`pytorch-native-cpu-2.5.1-linux-x86_64.jar`, `pytorch-native-cpu-2.5.1-osx-aarch64.jar`, etc.), Netty's `netty-tcnative-boringssl-static` per-platform JARs, and others. @@ -105,39 +129,43 @@ Build tools can be configured to resolve the correct classifier automatically: - **Uber-jar builds**: include all classifiers; the coordination artifact picks the right one at runtime. **Advantages:** -- Default download is the tiny coordination artifact (~1.5 MB) plus one platform JAR (~20–26 MB compressed) — approximately **22–28 MB total** vs. 132–180 MB for a monolithic JAR. + +* The long-term runtime-only download is the coordination artifact plus one platform JAR instead of every platform binary. - Each platform JAR changes independently; CI caches and Docker layers for unchanged platforms are preserved across releases. - Users building for a single known platform (most production deployments) pay exactly the cost of that platform. - Follows well-established Maven ecosystem conventions; standard tooling ([os-maven-plugin](#references), Gradle variant resolution) handles classifier selection. - Aligns with DJL's proven distribution strategy for large native ML runtimes. **Drawbacks:** + - Requires publishing 6–8 additional Maven artifacts per release. - Users building portable über-JARs must explicitly include all classifiers they wish to support. - Slightly more complex `pom.xml` / `build.gradle` for users who need cross-platform packaging. -### Option 3: Download-on-demand (DJL thin placeholder style) +### Option 3: Download on demand The SDK ships a minimal placeholder that detects the current platform at runtime and downloads the correct `runtime.node` binary from a distribution endpoint (GitHub Releases or a CDN) on first use, caching it locally (e.g., `~/.copilot/runtime-cache/`). **Advantages:** + - Zero native binary content in any published Maven artifact; total download at `mvn install` is negligible. - Identical user experience to the current "externally provided runtime" model during the download, which most CLI users already accept. **Drawbacks:** + - Requires internet access on first run. Offline environments (air-gapped enterprise, CI without outbound HTTP) break silently or require manual pre-seeding. - Introduces a network dependency into an otherwise pure library artifact, which violates Maven Central's expectations for reproducible builds. - Adds an operational concern: distribution endpoint availability, CDN costs, URL stability across versions. - Makes JVM startup non-deterministic in latency (first run downloads 20–26 MB). - Cannot be pre-warmed by dependency management tooling; no `mvn dependency:resolve` analogue works for a runtime download. -## Decision Outcome +## Decision outcome -**Chosen: Option 2 — per-platform classifier JARs and Option 1 - monolithic jar. Use `maven-assembly-plugin` to allow the creation of the monolithic jar.** +**Chosen: Option 2, per-platform classifier JARs, with Option 1 available through a consumer-built monolithic JAR.** Consumers can use `maven-assembly-plugin` to merge the platform classifiers they need. ### Rationale -1. **User download cost matches actual need.** Most users run on one OS and architecture. Option 2 makes their download approximately 22–28 MB (coordination JAR + one platform JAR), versus 132–180 MB for Option 1 and an unbounded deferred network cost for Option 3. +1. **User download cost matches actual need.** Most users run on one OS and architecture. Option 2 avoids downloading every platform artifact. During the active migration, each classifier also carries the embedded-host executable and is larger than the runtime-only target. 2. **Proven ecosystem pattern.** DJL, Netty, and others have established the per-classifier pattern as the correct Maven idiom for large native binaries. Build tooling already knows how to handle it; users and framework integrations (Spring Boot, Quarkus, Micronaut) are familiar with it. @@ -145,15 +173,46 @@ The SDK ships a minimal placeholder that detects the current platform at runtime 4. **No operational dependencies.** Unlike Option 3, no external download service is required at runtime. The artifact is self-contained once resolved by Maven/Gradle. -5. **Size per platform is acceptable.** At ~20–26 MB compressed per platform, each classifier JAR is well within the range of routinely used native JARs in the Java ecosystem (DJL PyTorch osx-aarch64: 37 MB; ONNX Runtime per platform: ~20–30 MB before bundling). +5. **The distribution model remains valid as artifact size changes.** The current transitional classifier is large because it contains both the runtime and CLI. The classifier model still prevents users from downloading artifacts for unrelated platforms, and its size decreases when the embedded-host executable is no longer required. 6. **Option 3 remains composable.** A download-on-demand fallback can be layered on top of Option 2 for users who prefer it without changing the primary distribution model. The coordination artifact can attempt classpath lookup first, then fall back to a cached download if no matching classifier JAR is present. -7. See section [How can we do Option 2 and Option 1](#how-can-we-do-option-2-and-option-1) for more details. +7. See [How to support classifier and monolithic JARs](#how-to-support-classifier-and-monolithic-jars) for more details. + +### Transport selection and failure behavior + +Adding a classifier JAR does not change the client's connection automatically. Users opt in with: + +```java +CopilotClientOptions options = new CopilotClientOptions() + .setConnection(RuntimeConnection.forInProcess()); +``` + +The `COPILOT_SDK_DEFAULT_CONNECTION=inprocess` environment variable also selects the in-process connection when no explicit or legacy subprocess options override it. + +The selected connection is strict: + +* If the user selects in-process and native resolution or startup fails, `CopilotClient.start()` fails. +* The SDK does not silently retry with stdio or TCP. +* If the user does not select in-process, classifier JARs are ignored and the existing stdio, TCP, or URI behavior remains unchanged. + +### Runtime resolution order + +When the in-process connection is selected, the Java loader resolves `runtime.node` in this order: + +1. `COPILOT_CLI_PATH`: accept either a flat sibling `runtime.node` or the npm `prebuilds//runtime.node` layout. +1. Classpath resource: extract `native//runtime.node` and the bundled CLI from the classifier or monolithic JAR into a cache keyed by SDK version, native package version, and classifier. +1. PATH compatibility fallback: find `copilot` on `PATH` and accept a flat sibling `runtime.node`. + +If none succeeds, startup fails. The PATH fallback does not claim to support every npm or Homebrew installation layout. + +### Current platform scope + +The platform detector recognizes the 8 classifiers listed in this ADR. The current Maven packaging and documented experimental support publish only `linux-x64`. Additional classifier artifacts remain follow-up work. ## Binding technology: JNA over Panama FFM -A secondary decision within the scope of this ADR is *how* the coordination artifact calls the C ABI entry points once the correct `runtime.node` binary has been loaded. Two candidates were considered: [JNA](#references) and the [Foreign Function & Memory API](#references) (FFM, the product of [Project Panama](#references), final since Java 22 via [JEP 454](#references)). +A secondary decision within the scope of this ADR is _how_ the coordination artifact calls the C ABI entry points once the correct `runtime.node` binary has been loaded. Two candidates were considered: [JNA](#references) and the [Foreign Function & Memory API](#references) (FFM, the product of [Project Panama](#references), final since Java 22 via [JEP 454](#references)). **Chosen: JNA.** FFM was considered and deliberately deferred, for the following reasons: @@ -161,7 +220,7 @@ A secondary decision within the scope of this ADR is *how* the coordination arti 2. **Consumer-side configuration burden.** FFM downcalls and upcalls are restricted operations under the JDK's integrity-by-default direction ([JEP 472](#references)). An FFM-based SDK would require every consumer to grant native access explicitly — `--enable-native-access=` (or `ALL-UNNAMED` for classpath applications) on the launcher, or an `Enable-Native-Access` manifest attribute. JNA requires no consumer-side configuration today. For an SDK, this flag becomes every downstream application's problem and a predictable source of support issues. (JNA is on the same enforcement trajectory eventually, as it uses JNI internally; this consideration buys time, not immunity.) -3. **No realizable performance benefit.** FFM's principal advantage over JNA is the elimination of per-call reflective marshalling overhead. The C ABI surface here is a fixed set of ~12 entry points carrying JSON-RPC strings; JSON serialization/deserialization cost dominates the call path, and call frequency is bounded by agent-interaction rates rather than tight loops. The latency difference between JNA and FFM is expected to be unmeasurable in end-to-end SDK usage. This calculus would change only if the transport moved to a high-frequency or shared-memory framing model. +3. **No realizable performance benefit.** FFM's principal advantage over JNA is the elimination of per-call reflective marshalling overhead. The C ABI surface here is a fixed set of 5 entry points carrying JSON-RPC bytes; JSON serialization and deserialization cost dominates the call path, and call frequency is bounded by agent-interaction rates rather than tight loops. The latency difference between JNA and FFM is expected to be unmeasurable in end-to-end SDK usage. This calculus would change only if the transport moved to a high-frequency or shared-memory framing model. 4. **Upcall lifetime complexity.** The transport is bidirectional: the runtime delivers JSON-RPC responses and server-initiated requests back into Java from native threads. JNA's `Callback` mechanism handles foreign-thread attachment with well-established semantics. FFM upcall stubs require explicit `Arena` lifetime management, where a stub whose arena is closed while the Rust side still holds the function pointer results in a JVM crash. This shifts lifetime reasoning that JNA encapsulates onto the binding layer. @@ -171,110 +230,74 @@ A secondary decision within the scope of this ADR is *how* the coordination arti ### Preserving the FFM migration path -FFM is regarded as the likely eventual binding technology: the JEP 472 endgame applies enforcement pressure to JNA as well, and a ~12-function stable C ABI makes a future migration inexpensive. To keep that path open at low cost: +FFM is regarded as the likely eventual binding technology: the JEP 472 endgame applies enforcement pressure to JNA as well, and a 5-function stable C ABI makes a future migration inexpensive. To keep that path open at low cost: - The binding layer is abstracted behind a small internal interface (native load + downcall + upcall registration), so that an FFM implementation can be introduced later — for example, as a multi-release JAR selecting FFM on Java 22+ — without changes to the transport or API layers. - The decision should be revisited when (a) the SDK's minimum Java baseline moves past 17, or (b) JDK releases begin enforcing `--illegal-native-access=deny` by default, whichever comes first. -## How can we do Option 2 and Option 1 +## How to support classifier and monolithic JARs -## How it works: classpath resource convention + platform detection +### Classpath resource convention and platform detection -### 1. Each classifier JAR uses a well-known resource path +#### Each classifier JAR uses a well-known resource path -Each per-platform JAR (`copilot-sdk-java-runtime:VERSION:darwin-arm64`, etc.) places its binary under a deterministic path inside the JAR: +Each per-platform JAR places its artifacts under a deterministic path: ``` native/darwin-arm64/runtime.node native/darwin-arm64/platform.properties +native/darwin-arm64/copilot ``` -When `maven-assembly-plugin` creates the uber-jar, it unpacks all dependencies and merges them. The resulting uber-jar contains: +Windows classifiers use `copilot.exe`. The CLI entrypoint remains in the classifier while the runtime requires the transitional embedded host. + +When `maven-assembly-plugin` creates the uber-JAR, it unpacks all dependencies and merges them. The resulting uber-JAR contains the selected platforms: ``` com/github/copilot/sdk/... (Java classes) native/linux-x64/runtime.node +native/linux-x64/copilot native/linux-arm64/runtime.node +native/linux-arm64/copilot native/linuxmusl-x64/runtime.node +native/linuxmusl-x64/copilot native/linuxmusl-arm64/runtime.node +native/linuxmusl-arm64/copilot native/darwin-x64/runtime.node +native/darwin-x64/copilot native/darwin-arm64/runtime.node +native/darwin-arm64/copilot native/win32-x64/runtime.node +native/win32-x64/copilot.exe native/win32-arm64/runtime.node +native/win32-arm64/copilot.exe ``` -### 2. The coordination artifact selects at runtime via `getResourceAsStream` +#### The coordination artifact selects at runtime through the classloader -```java -public class NativeRuntimeLoader { - - public Path loadRuntime() { - String classifier = detectPlatformClassifier(); - String resourcePath = "native/" + classifier + "/runtime.node"; - - try (InputStream in = getClass().getClassLoader() - .getResourceAsStream(resourcePath)) { - if (in == null) { - throw new UnsupportedOperationException( - "No native runtime for platform: " + classifier); - } - Path cached = getCachePath(classifier); - if (!Files.exists(cached)) { - Files.createDirectories(cached.getParent()); - Files.copy(in, cached); - // Make executable on Unix - cached.toFile().setExecutable(true); - } - return cached; - } - } - - private String detectPlatformClassifier() { - String os = normalizeOs(System.getProperty("os.name")); - String arch = normalizeArch(System.getProperty("os.arch")); - String libc = "linux".equals(os) ? detectLinuxLibc() : ""; - - // Produces: "linux-x64", "linuxmusl-arm64", "darwin-arm64", "win32-x64", etc. - return (libc.isEmpty() ? os : os + libc) + "-" + arch; - } - - private String detectLinuxLibc() { - // Read ELF PT_INTERP from /proc/self/exe - // If interpreter contains "/ld-musl-" → "musl" - // Otherwise → "" (glibc is the default/unmarked case for "linux-") - // ... - } - - private Path getCachePath(String classifier) { - String version = getClass().getPackage().getImplementationVersion(); - return Path.of(System.getProperty("user.home"), - ".copilot", "runtime-cache", version, classifier, "runtime.node"); - } -} -``` +`NativeRuntimeLoader` detects the current classifier and requests `native//runtime.node`, `native//platform.properties`, and `native//copilot` from the classloader. It uses the native package version from `platform.properties` as part of the cache identity, writes each executable artifact to a unique sibling temporary file, forces the file contents to storage, and atomically publishes the completed file into `~/.copilot/runtime-cache////`. + +On non-Windows platforms, the loader makes the temporary CLI executable and verifies its executable status before atomic publication. A nonempty but non-executable cached CLI is repaired instead of being accepted as valid. -### 3. JNA loads from the extracted path +#### JNA loads from the extracted path Once extracted to a known filesystem path, JNA loads it directly: ```java -NativeLibrary lib = NativeLibrary.getInstance(extractedPath.toString()); -// Or via a mapped interface: -CopilotRuntime runtime = Native.load(extractedPath.toString(), CopilotRuntime.class); +CopilotRuntimeLibrary runtime = + Native.load(extractedPath.toString(), CopilotRuntimeLibrary.class); ``` -### Key insight: the same code works in both modes +#### The same code works in both modes -The beauty is that `getResourceAsStream("native/darwin-arm64/runtime.node")` works identically whether: +Classloader resource lookup works identically whether: -- The native lives in a **separate classifier JAR** on the classpath (normal dev dependency), OR -- It's been **merged into an uber-jar** by `maven-assembly-plugin` +* The native artifacts live in a separate classifier JAR on the classpath. +* The artifacts have been merged into an uber-JAR by `maven-assembly-plugin`. -The classloader doesn't care which JAR file the resource came from — it searches the entire classpath. This means **zero code changes** between the two consumption models. +The classloader searches the entire classpath, so the Java loading code does not change between the two consumption models. ---- - -## Assembly plugin configuration (consumer-side) +### Consumer-side assembly plugin configuration A consumer building a portable uber-jar would configure: @@ -298,7 +321,7 @@ With all classifier JARs declared as dependencies: copilot-sdk-java ${copilot.version} - + com.github copilot-sdk-java-runtime @@ -311,66 +334,63 @@ With all classifier JARs declared as dependencies: ${copilot.version} darwin-arm64 - + ``` ---- - -## Why this works cleanly +### Why this works cleanly -| Concern | How it's handled | -|---------|-----------------| -| No resource path collisions | Each platform has its own subdirectory (`native//`) | -| Extraction only happens once | Cached to `~/.copilot/runtime-cache///` | -| Works without uber-jar too | Same `getResourceAsStream` call — classloader finds it in the separate JAR | -| Subset selection | Consumer declares only the classifiers they need; missing platforms get a clear error at runtime | -| JNA loading | `NativeLibrary.getInstance(path)` loads from an absolute filesystem path after extraction — no JNA platform-detection magic needed | - -The pattern is identical to how DJL's `LibUtils.loadLibrary()` works — detect platform, construct resource path, extract if needed, load via absolute path. +| Concern | How it's handled | +| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| No resource path collisions | Each platform has its own subdirectory (`native//`) | +| Extraction only happens once | Cached to `~/.copilot/runtime-cache////` | +| Works without uber-JAR too | The classloader finds the same resource in a separate classifier JAR | +| Subset selection | Consumer declares only the classifiers they need; missing platforms get a clear error at runtime | +| JNA loading | `Native.load(path, interface)` loads from an absolute filesystem path after extraction | +The pattern follows DJL's `LibUtils.loadLibrary()` approach: detect the platform, construct the resource path, extract when needed, and load from an absolute path. ## Consequences -- A new Maven module (`copilot-sdk-java-runtime` or similar) is introduced to hold the per-platform native JARs. The existing `copilot-sdk-java` coordination artifact depends on it. -- The coordination artifact gains a platform detection and native loading component that: +* The `copilot-sdk-java-runtime` Maven module holds the per-platform classifier JARs. Users add the classifier for each platform they intend to run. +* Users selecting in-process mode also add JNA. The coordination artifact does not force native dependencies on users who keep the default subprocess connection. +* The coordination artifact includes platform detection and native loading code that: 1. Detects OS, architecture, and Linux libc variant deterministically as described above. 2. Locates the matching `runtime.node` binary on the classpath (via `getResourceAsStream` from the classifier JAR). - 3. Extracts the binary to a temporary or cached location (e.g., `~/.copilot/runtime-cache/`) if not already present. + 3. Extracts `runtime.node` and the transitional CLI entrypoint into `~/.copilot/runtime-cache/` if valid cached files are not already present. 4. Loads it via [JNA](#references) using the C ABI entry points, per the [binding technology decision](#binding-technology-jna-over-panama-ffm) above. The JNA-specific code is confined behind an internal binding interface to preserve a future FFM migration path. -- The release pipeline for `github/copilot-agent-runtime` must produce the per-platform `runtime.node` binaries as inputs to the Java SDK publish workflow. The per-platform `pkg-tarballs-` artifacts from the `publish-cli.yml` workflow are the authoritative source. -- Each release of `copilot-sdk-java` publishes 6 (or 8) classifier JARs to Maven Central alongside the coordination JAR. -- The version of the bundled `runtime.node` is recorded in the coordination JAR's manifest and queryable at runtime, enabling diagnostics and mismatch detection. -- `cli-native.node` is not bundled. It provides only terminal UI features (ICU4X text segmentation, Win32 APIs, OS desktop notifications) that are irrelevant to the Java SDK's programmatic API surface. +* The Java build fetches the pinned `@github/copilot-` npm package, verifies its SHA-512 integrity from `nodejs/package-lock.json`, and packages the version-matched runtime and CLI files. +* The current release work publishes the `linux-x64` classifier. The planned classifier set expands to the other detected platforms. +* `cli-native.node` is not bundled. It provides terminal UI features that are irrelevant to the Java SDK's programmatic API surface. ## Related work items -- https://github.com/github/copilot-sdk/issues/1917 — Epic: Embed Rust-based Copilot CLI Runtime and cease requiring Node.js -- https://devdiv.visualstudio.com/DevDiv/_workitems/edit/3028097 -- https://github.com/github/copilot-sdk/pull/1901 dotnet: in-process FFI runtime hosting (InProcess transport) -- https://github.com/github/copilot-sdk/pull/1915 Add in-process FFI transport for Rust and TypeScript SDKs +* https://github.com/github/copilot-sdk/issues/1917: Epic to embed the Rust-based Copilot CLI runtime +* https://devdiv.visualstudio.com/DevDiv/_workitems/edit/3028097 +* https://github.com/github/copilot-sdk/pull/1901: .NET in-process FFI runtime hosting +* https://github.com/github/copilot-sdk/pull/1915: In-process FFI transport for Rust and TypeScript SDKs ### References -| Term | Definition | Link | -|------|------------|------| -| **FFI** (Foreign Function Interface) | A mechanism by which code written in one language can call functions defined in another. In this ADR, Java calls into the Rust runtime shared library via JNA's FFI layer. | https://en.wikipedia.org/wiki/Foreign_function_interface | -| **JNA** (Java Native Access) | A Java library that provides easy access to native shared libraries without requiring the JNI boilerplate. Used here to call the `extern "C"` C ABI entry points exported by `runtime.node`. | https://github.com/java-native-access/jna | -| **napi-rs** | A Rust framework for building native Node.js addons using the Node-API (napi) stable ABI. Produces the `.node` file and generates TypeScript type declarations automatically. | https://napi.rs/ | -| **cdylib** | A Rust `crate-type` that produces a C-compatible dynamic shared library (`.so` / `.dylib` / `.dll`). Distinct from `dylib` (Rust-to-Rust only) and `staticlib`. | https://doc.rust-lang.org/reference/linkage.html | -| **napi (Node-API)** | A stable C ABI provided by Node.js for building native addons that remain binary-compatible across Node.js versions. `napi-rs` generates Rust code against this interface. | https://nodejs.org/api/n-api.html | -| **C ABI** (Application Binary Interface) | The low-level contract between a compiled binary and its callers: calling conventions, data type layouts, symbol naming. An `extern "C"` ABI uses C's conventions, making a library callable from any language that speaks C FFI. | https://en.wikipedia.org/wiki/Application_binary_interface | -| **ELF PT_INTERP** | A segment in an [ELF](https://man7.org/linux/man-pages/man5/elf.5.html) binary (the Linux/Unix executable format) that records the path of the dynamic linker/interpreter. On glibc systems this path is `/lib64/ld-linux-x86-64.so.2`; on musl systems it is `/lib/ld-musl-x86_64.so.1`. Inspecting it is the most reliable way to detect glibc vs. musl at runtime without executing a subprocess. | https://man7.org/linux/man-pages/man5/elf.5.html | -| **glibc** (GNU C Library) | The standard C runtime library on most mainstream Linux distributions (Debian, Ubuntu, RHEL, Fedora, SLES). Binaries linked against glibc require the same version or newer to be present at runtime. The `runtime.node` glibc build requires glibc ≥ 2.28. | https://www.gnu.org/software/libc/ | -| **musl libc** | An alternative C standard library optimised for static linking and used as the default libc on Alpine Linux. Not binary-compatible with glibc; a separate `runtime.node` build is required. | https://musl.libc.org/ | -| **MSVC CRT** (Microsoft Visual C++ Runtime) | The C runtime library shipped with Visual Studio. When compiled with `+crt-static` (as `runtime.node` is on Windows), it is statically linked into the binary and the end-user does not need to install the Visual C++ Redistributable. | https://learn.microsoft.com/en-us/cpp/c-runtime-library/c-run-time-library-reference | -| **Project Panama** | The OpenJDK project that produced the Foreign Function & Memory API as the modern, supported replacement for JNI-based native interop. | https://openjdk.org/projects/panama/ | -| **FFM** (Foreign Function & Memory API) | The `java.lang.foreign` API for calling native functions and managing native memory from Java, finalized in Java 22. Considered and deferred as the binding technology for this SDK; see [Binding technology](#binding-technology-jna-over-panama-ffm). | https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html | -| **JEP 454** | The JDK Enhancement Proposal that finalized the FFM API in Java 22. | https://openjdk.org/jeps/454 | -| **JEP 472** | "Prepare to Restrict the Use of JNI" — part of the JDK's integrity-by-default direction under which native access (via JNI or FFM) requires explicit consumer opt-in (`--enable-native-access`). Drives both the FFM configuration-burden concern and the expectation that JNA itself will eventually require the same opt-in. | https://openjdk.org/jeps/472 | -| **DJL** (Deep Java Library) | Amazon's open-source Java framework for ML inference, used here as a reference for the per-platform classifier JAR distribution pattern. Its PyTorch native artifacts (`pytorch-native-cpu-*-.jar`) are the direct model for the proposed `copilot-sdk-java-runtime:VERSION:` artifacts. | https://djl.ai/ | -| **os-maven-plugin** | A Maven extension that detects the current OS and architecture and exposes them as properties (e.g., `${os.detected.classifier}`) so that `` values can be resolved at build time rather than hardcoded. | https://github.com/trustin/os-maven-plugin | -| **ONNX Runtime** | Microsoft's cross-platform ML inference runtime, used in this ADR as the size comparable for a monolithic all-platform JAR (~130 MB, Option 1). | https://onnxruntime.ai/ | +| Term | Definition | Link | +| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| **FFI** (Foreign Function Interface) | A mechanism by which code written in one language can call functions defined in another. In this ADR, Java calls into the Rust runtime shared library via JNA's FFI layer. | https://en.wikipedia.org/wiki/Foreign_function_interface | +| **JNA** (Java Native Access) | A Java library that provides easy access to native shared libraries without requiring the JNI boilerplate. Used here to call the `extern "C"` C ABI entry points exported by `runtime.node`. | https://github.com/java-native-access/jna | +| **napi-rs** | A Rust framework for building native Node.js addons using the Node-API (napi) stable ABI. Produces the `.node` file and generates TypeScript type declarations automatically. | https://napi.rs/ | +| **cdylib** | A Rust `crate-type` that produces a C-compatible dynamic shared library (`.so` / `.dylib` / `.dll`). Distinct from `dylib` (Rust-to-Rust only) and `staticlib`. | https://doc.rust-lang.org/reference/linkage.html | +| **napi (Node-API)** | A stable C ABI provided by Node.js for building native addons that remain binary-compatible across Node.js versions. `napi-rs` generates Rust code against this interface. | https://nodejs.org/api/n-api.html | +| **C ABI** (Application Binary Interface) | The low-level contract between a compiled binary and its callers: calling conventions, data type layouts, symbol naming. An `extern "C"` ABI uses C's conventions, making a library callable from any language that speaks C FFI. | https://en.wikipedia.org/wiki/Application_binary_interface | +| **ELF PT_INTERP** | A segment in an [ELF](https://man7.org/linux/man-pages/man5/elf.5.html) binary (the Linux/Unix executable format) that records the path of the dynamic linker/interpreter. On glibc systems this path is `/lib64/ld-linux-x86-64.so.2`; on musl systems it is `/lib/ld-musl-x86_64.so.1`. Inspecting it is the most reliable way to detect glibc vs. musl at runtime without executing a subprocess. | https://man7.org/linux/man-pages/man5/elf.5.html | +| **glibc** (GNU C Library) | The standard C runtime library on most mainstream Linux distributions (Debian, Ubuntu, RHEL, Fedora, SLES). Binaries linked against glibc require the same version or newer to be present at runtime. The `runtime.node` glibc build requires glibc ≥ 2.28. | https://www.gnu.org/software/libc/ | +| **musl libc** | An alternative C standard library optimised for static linking and used as the default libc on Alpine Linux. Not binary-compatible with glibc; a separate `runtime.node` build is required. | https://musl.libc.org/ | +| **MSVC CRT** (Microsoft Visual C++ Runtime) | The C runtime library shipped with Visual Studio. When compiled with `+crt-static` (as `runtime.node` is on Windows), it is statically linked into the binary and the end-user does not need to install the Visual C++ Redistributable. | https://learn.microsoft.com/en-us/cpp/c-runtime-library/c-run-time-library-reference | +| **Project Panama** | The OpenJDK project that produced the Foreign Function & Memory API as the modern, supported replacement for JNI-based native interop. | https://openjdk.org/projects/panama/ | +| **FFM** (Foreign Function & Memory API) | The `java.lang.foreign` API for calling native functions and managing native memory from Java, finalized in Java 22. Considered and deferred as the binding technology for this SDK; see [Binding technology](#binding-technology-jna-over-panama-ffm). | https://docs.oracle.com/en/java/javase/22/core/foreign-function-and-memory-api.html | +| **JEP 454** | The JDK Enhancement Proposal that finalized the FFM API in Java 22. | https://openjdk.org/jeps/454 | +| **JEP 472** | "Prepare to Restrict the Use of JNI" — part of the JDK's integrity-by-default direction under which native access (via JNI or FFM) requires explicit consumer opt-in (`--enable-native-access`). Drives both the FFM configuration-burden concern and the expectation that JNA itself will eventually require the same opt-in. | https://openjdk.org/jeps/472 | +| **DJL** (Deep Java Library) | Amazon's open-source Java framework for ML inference, used here as a reference for the per-platform classifier JAR distribution pattern. Its PyTorch native artifacts (`pytorch-native-cpu-*-.jar`) are the direct model for the proposed `copilot-sdk-java-runtime:VERSION:` artifacts. | https://djl.ai/ | +| **os-maven-plugin** | A Maven extension that detects the current OS and architecture and exposes them as properties (e.g., `${os.detected.classifier}`) so that `` values can be resolved at build time rather than hardcoded. | https://github.com/trustin/os-maven-plugin | +| **ONNX Runtime** | Microsoft's cross-platform ML inference runtime, used in this ADR as the size comparable for a monolithic all-platform JAR (~130 MB, Option 1). | https://onnxruntime.ai/ | Additional source references: @@ -381,4 +401,3 @@ Additional source references: - `github/copilot-agent-runtime` build target definitions: `script/build-runtime.ts` - `github/copilot-agent-runtime` glibc sysroot and verification: `script/linux/install-sysroot.cjs`, `script/linux/verify-glibc-requirements.sh` - ONNX Runtime Java on Maven Central (size comparable): https://repo1.maven.org/maven2/com/microsoft/onnxruntime/onnxruntime/1.21.0/ - diff --git a/java/pom.xml b/java/pom.xml index 796051891..1fcbcca3f 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,12 +6,12 @@ 4.0.0 com.github - copilot-sdk-java + copilot-sdk-java-parent 1.0.11-preview.0-SNAPSHOT - jar + pom - GitHub Copilot SDK :: Java - Official SDK for programmatic control of GitHub Copilot CLI + GitHub Copilot SDK :: Java :: Parent + Parent POM for the GitHub Copilot Java SDK multi-module reactor https://github.com/github/copilot-sdk @@ -36,50 +36,25 @@ HEAD - - - central - https://central.sonatype.com/repository/maven-snapshots/ - - + + sdk + copilot-native + + + 17 UTF-8 ${project.basedir}/.. - ${copilot.sdk.root}/test - - ${copilot.sdk.root}/nodejs/node_modules/@github/copilot/npm-loader.js - - false - - ${skip.test.harness} - - notice - - ^1.0.79 - + + true - - - - com.fasterxml.jackson.core - jackson-databind - 2.22.1 - - - com.fasterxml.jackson.core - jackson-annotations - 2.22 - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - 2.22.1 - - - - - com.github.spotbugs - spotbugs-annotations - 4.10.3 - provided - - - - - org.junit.jupiter - junit-jupiter - 5.14.4 - test - - - org.mockito - mockito-core - 5.23.0 - test - - - + + org.apache.maven.plugins + maven-clean-plugin + 3.5.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.5.1 + org.apache.maven.plugins maven-javadoc-plugin @@ -146,521 +96,111 @@ none + + org.apache.maven.plugins + maven-source-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.6 + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.5.6 + + + org.apache.maven.plugins + maven-antrun-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.6.3 + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.8 + + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + com.github.spotbugs spotbugs-maven-plugin 4.10.3.0 + + + com.diffplug.spotless + spotless-maven-plugin + 2.46.1 + + + org.jacoco + jacoco-maven-plugin + 0.8.15 + + + org.codehaus.mojo + exec-maven-plugin + 3.6.3 + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.1 + + + org.sonatype.central + central-publishing-maven-plugin + 0.11.0 + + + org.codehaus.mojo + flatten-maven-plugin + 1.7.0 - config/spotbugs/spotbugs-exclude.xml + ossrh + + + flatten + process-resources + + flatten + + + + flatten-clean + clean + + clean + + + - - org.apache.maven.plugins - maven-clean-plugin - 3.5.0 - - - org.apache.maven.plugins - maven-compiler-plugin - 3.15.0 - - - -Acopilot.experimental.allowed=true - - none - - - - org.apache.maven.plugins - maven-jar-plugin - 3.5.1 - - - org.apache.maven.plugins - maven-antrun-plugin - 3.2.0 - - - print-test-jdk-banner - process-test-classes - - run - - - - - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.6.3 - - - install-harness-dependencies - generate-test-resources - - exec - - - ${skip.test.harness} - npm - ${copilot.sdk.root}/test/harness - - ci - --loglevel - ${npm.loglevel} - - - - - - install-nodejs-cli-dependencies - generate-test-resources - - exec - - - ${skip.cli.install} - npm - ${copilot.sdk.root}/nodejs - - ci - --ignore-scripts - --loglevel - ${npm.loglevel} - - - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 3.5.6 - - - - integration-test - verify - - - - - - ${project.build.directory} - ${project.build.finalName} - ${project.build.testOutputDirectory} - - - - ${copilot.cli.path} - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.5.6 - - alphabetical - - ${testExecutionAgentArgs} ${surefire.jvm.args} - - 2 - - ${copilot.tests.dir} - ${copilot.sdk.root} - - - - ${copilot.cli.path} - - - - - - isolated-resume-tests - test - - test - - - isolated-resume - - ${project.build.directory}/surefire-reports-isolated - - - - - default-test - - isolated-resume - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.1 - - - add-generated-source - generate-sources - - add-source - - - - ${project.basedir}/src/generated/java - - - - - - - com.diffplug.spotless - spotless-maven-plugin - 2.46.1 - - - - src/generated/java/**/*.java - - - 4.33 - - - - - - true - 4 - - - - - - - org.jacoco - jacoco-maven-plugin - 0.8.15 - - - - wire-up-coverage-instrumentation - - prepare-agent - - - - ${project.build.directory}/jacoco-test-results/sdk-tests.exec - - testExecutionAgentArgs - - - com/github/copilot/** - - - com/github/copilot/E2ETestContext* - com/github/copilot/CapiProxy* - - - - - - build-coverage-report-from-tests - - report - - verify - - ${project.build.directory}/jacoco-test-results/sdk-tests.exec - ${project.reporting.outputDirectory}/jacoco-coverage - - - META-INF/versions/**/*.class - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.6.0 - - config/checkstyle/checkstyle.xml - true - true - false - - - - validate - validate - - check - - - - - - com.puppycrawl.tools - checkstyle - 10.26.1 - - - - - - org.sonatype.central - central-publishing-maven-plugin - 0.11.0 - true - - central - true - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.6.3 - - - enforce-jdk25 - - enforce - - - - - [25,) - JDK 25+ is required to build the Multi-Release JAR with the virtual-thread overlay. - - - - - - verify-multi-release-overlay - verify - - enforce - - - - - - ${project.build.outputDirectory}/META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class - - Multi-Release JAR overlay missing: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class was not compiled. Ensure the build runs on JDK 25+. - - - - - + flatten-maven-plugin - - - jdk21+ - - [21,) - - - -XX:+EnableDynamicAgentLoading - - - - java25-multi-release - - [25,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - compile-java25 - compile - - compile - - - 25 - false - - ${project.basedir}/src/main/java25 - - true - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - true - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - verify-java25-overlay - package - - run - - - - - - - - - -JDK 25 multi-release overlay class is missing from the packaged JAR. -Expected entry: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class -JAR: ${project.build.directory}/${project.build.finalName}.jar - -This usually means the 'java25-multi-release' Maven profile did not activate -(e.g. the build is running on a JDK older than 25) or maven-compiler-plugin -did not produce the multi-release output. Re-build on JDK 25+ and verify the -'compile-java25' execution ran during the 'compile' phase. - - - - - - - - - - - - skip-test-harness - - true - - - - - skip-cli-install-when-tests-skipped - - - skipTests - true - - - - true - - - - - skip-cli-install-when-maven-test-skip - - - maven.test.skip - true - - - - true - - - - - debug - - - - org.apache.maven.plugins - maven-surefire-plugin - - - ${project.basedir}/src/test/resources/logging-debug.properties - - - - - - release @@ -668,7 +208,6 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the org.apache.maven.plugins maven-source-plugin - 3.4.0 attach-sources @@ -693,7 +232,6 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the org.apache.maven.plugins maven-gpg-plugin - 3.2.8 sign-artifacts @@ -707,103 +245,5 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the - - - update-schemas-from-npm-artifact - - - - org.codehaus.mojo - exec-maven-plugin - 3.6.3 - - - update-copilot-schema-version - generate-sources - - exec - - - npm - ${project.basedir}/scripts/codegen - - install - @github/copilot@${copilot.schema.version} - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.6.3 - - - require-schema-version - validate - - enforce - - - - - copilot.schema.version - You must specify -Dcopilot.schema.version=VERSION (e.g. 1.0.25) - - - - - - - - - - - - codegen - - - - org.codehaus.mojo - exec-maven-plugin - 3.6.3 - - - codegen-npm-install - generate-sources - - exec - - - npm - ${project.basedir}/scripts/codegen - - ci - - - - - codegen-generate - generate-sources - - exec - - - npm - ${project.basedir}/scripts/codegen - - run - generate - - - - - - - - diff --git a/java/scripts/codegen/java.ts b/java/scripts/codegen/java.ts index 9f7eb4003..3bdc51d03 100644 --- a/java/scripts/codegen/java.ts +++ b/java/scripts/codegen/java.ts @@ -4,7 +4,7 @@ /** * Java code generator for session-events and RPC types. - * Generates Java source files under src/generated/java/ from JSON Schema files. + * Generates Java source files under sdk/src/generated/java/ from JSON Schema files. */ import fs from "fs/promises"; @@ -779,7 +779,7 @@ async function generateSessionEvents(schemaPath: string): Promise { const variants = extractEventVariants(schema); const packageName = "com.github.copilot.generated"; - const packageDir = `src/generated/java/com/github/copilot/generated`; + const packageDir = `sdk/src/generated/java/com/github/copilot/generated`; // Generate base SessionEvent class await generateSessionEventBaseClass(variants, packageName, packageDir); @@ -1384,7 +1384,7 @@ async function generateRpcTypes(schemaPath: string): Promise { } const packageName = "com.github.copilot.generated.rpc"; - const packageDir = `src/generated/java/com/github/copilot/generated/rpc`; + const packageDir = `sdk/src/generated/java/com/github/copilot/generated/rpc`; // Collect all RPC methods from all sections const sections: [string, Record][] = []; @@ -2224,7 +2224,7 @@ async function generateRpcWrappers(schemaPath: string): Promise { currentDefinitions = (schema.definitions ?? {}) as Record; const packageName = "com.github.copilot.generated.rpc"; - const packageDir = `src/generated/java/com/github/copilot/generated/rpc`; + const packageDir = `sdk/src/generated/java/com/github/copilot/generated/rpc`; // RpcCaller interface and shared ObjectMapper holder await generateRpcCallerInterface(packageName, packageDir); @@ -2347,7 +2347,7 @@ async function main(): Promise { console.log("============================"); // Clean the generated output directory to remove orphaned files from previous runs - const generatedOutputDir = path.join(REPO_ROOT, "src/generated/java/com/github/copilot/generated"); + const generatedOutputDir = path.join(REPO_ROOT, "sdk/src/generated/java/com/github/copilot/generated"); console.log(`🧹 Cleaning output directory: ${generatedOutputDir}`); await fs.rm(generatedOutputDir, { recursive: true, force: true }); await fs.mkdir(generatedOutputDir, { recursive: true }); @@ -2362,8 +2362,8 @@ async function main(): Promise { await generateRpcWrappers(apiSchemaPath); // Generate package-info.java for each generated package - const generatedPkgDir = `src/generated/java/com/github/copilot/generated`; - const rpcPkgDir = `src/generated/java/com/github/copilot/generated/rpc`; + const generatedPkgDir = `sdk/src/generated/java/com/github/copilot/generated`; + const rpcPkgDir = `sdk/src/generated/java/com/github/copilot/generated/rpc`; await generateGeneratedPackageInfo(generatedPkgDir); await generateRpcPackageInfo(rpcPkgDir); diff --git a/java/config/checkstyle/checkstyle.xml b/java/sdk/config/checkstyle/checkstyle.xml similarity index 100% rename from java/config/checkstyle/checkstyle.xml rename to java/sdk/config/checkstyle/checkstyle.xml diff --git a/java/config/spotbugs/spotbugs-exclude.xml b/java/sdk/config/spotbugs/spotbugs-exclude.xml similarity index 100% rename from java/config/spotbugs/spotbugs-exclude.xml rename to java/sdk/config/spotbugs/spotbugs-exclude.xml diff --git a/java/jbang-example.java b/java/sdk/jbang-example.java similarity index 100% rename from java/jbang-example.java rename to java/sdk/jbang-example.java diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml new file mode 100644 index 000000000..374ca06b7 --- /dev/null +++ b/java/sdk/pom.xml @@ -0,0 +1,798 @@ + + + + 4.0.0 + + + com.github + copilot-sdk-java-parent + 1.0.11-preview.0-SNAPSHOT + ../pom.xml + + + com.github + copilot-sdk-java + jar + + GitHub Copilot SDK :: Java + Official SDK for programmatic control of GitHub Copilot CLI + https://github.com/github/copilot-sdk + + + scm:git:https://github.com/github/copilot-sdk.git + scm:git:https://github.com/github/copilot-sdk.git + https://github.com/github/copilot-sdk + HEAD + + + + + central + https://central.sonatype.com/repository/maven-snapshots/ + + + + + + ${project.basedir}/../.. + ${copilot.sdk.root}/test + + ${copilot.sdk.root}/nodejs/node_modules/@github/copilot/npm-loader.js + + ${copilot.sdk.root}/nodejs/node_modules/@github/copilot-linux-x64/copilot + + false + + ${skip.test.harness} + + notice + + + + false + + 5.19.1 + + + + + + com.fasterxml.jackson.core + jackson-databind + 2.22.1 + + + com.fasterxml.jackson.core + jackson-annotations + 2.22 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.22.1 + + + + + com.github.spotbugs + spotbugs-annotations + 4.10.3 + provided + + + + + net.java.dev.jna + jna + ${jna.version} + true + + + + + org.junit.jupiter + junit-jupiter + 5.14.4 + test + + + org.mockito + mockito-core + 5.23.0 + test + + + + + + + src/main/resources + true + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + config/spotbugs/spotbugs-exclude.xml + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -Acopilot.experimental.allowed=true + + none + + + + org.apache.maven.plugins + maven-antrun-plugin + + + print-test-jdk-banner + process-test-classes + + run + + + + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + install-harness-dependencies + generate-test-resources + + exec + + + ${skip.test.harness} + npm + ${copilot.sdk.root}/test/harness + + ci + --loglevel + ${npm.loglevel} + + + + + + install-nodejs-cli-dependencies + generate-test-resources + + exec + + + ${skip.cli.install} + npm + ${copilot.sdk.root}/nodejs + + ci + --ignore-scripts + --loglevel + ${npm.loglevel} + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + ${project.build.directory} + ${project.build.finalName} + ${project.build.testOutputDirectory} + + + + ${copilot.cli.path} + + + + + org.apache.maven.plugins + maven-surefire-plugin + + alphabetical + + + ${testExecutionAgentArgs} ${surefire.jvm.args} --add-opens com.github.copilot.java/com.github.copilot.e2e=ALL-UNNAMED + + 2 + + ${copilot.tests.dir} + ${copilot.sdk.root} + + + + ${copilot.cli.path} + + + + + + isolated-resume-tests + test + + test + + + isolated-resume + + ${project.build.directory}/surefire-reports-isolated + + + + + default-test + + isolated-resume + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-source + generate-sources + + add-source + + + + ${project.basedir}/src/generated/java + + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + + src/generated/java/**/*.java + + + 4.33 + + + + + + true + 4 + + + + + + + org.jacoco + jacoco-maven-plugin + + + + wire-up-coverage-instrumentation + + prepare-agent + + + + ${project.build.directory}/jacoco-test-results/sdk-tests.exec + + testExecutionAgentArgs + + + com/github/copilot/** + + + com/github/copilot/E2ETestContext* + com/github/copilot/CapiProxy* + + + + + + build-coverage-report-from-tests + + report + + verify + + ${project.build.directory}/jacoco-test-results/sdk-tests.exec + ${project.reporting.outputDirectory}/jacoco-coverage + + + META-INF/versions/**/*.class + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + config/checkstyle/checkstyle.xml + true + true + false + + + + validate + validate + + check + + + + + + com.puppycrawl.tools + checkstyle + 10.26.1 + + + + + + org.sonatype.central + central-publishing-maven-plugin + true + + central + true + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-jdk25 + + enforce + + + + + [25,) + JDK 25+ is required to build the Multi-Release JAR with the virtual-thread overlay. + + + + + + verify-multi-release-overlay + verify + + enforce + + + + + + ${project.build.outputDirectory}/META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class + + Multi-Release JAR overlay missing: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class was not compiled. Ensure the build runs on JDK 25+. + + + + + + + + + + + + + jdk21+ + + [21,) + + + -XX:+EnableDynamicAgentLoading + + + + java25-multi-release + + [25,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile-java25 + compile + + compile + + + 25 + false + + ${project.basedir}/src/main/java25 + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-java25-overlay + package + + run + + + + + + + + + +JDK 25 multi-release overlay class is missing from the packaged JAR. +Expected entry: META-INF/versions/25/com/github/copilot/InternalExecutorProvider.class +JAR: ${project.build.directory}/${project.build.finalName}.jar + +This usually means the 'java25-multi-release' Maven profile did not activate +(e.g. the build is running on a JDK older than 25) or maven-compiler-plugin +did not produce the multi-release output. Re-build on JDK 25+ and verify the +'compile-java25' execution ran during the 'compile' phase. + + + + + + + + + + + + skip-test-harness + + true + + + + + inprocess + + inprocess + + + + + com.github + copilot-sdk-java-runtime + ${project.version} + linux-x64 + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + 1 + none + + inprocess + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + 1 + none + + ${copilot.inprocess.cli.path} + inprocess + + + + + + + + + skip-cli-install-when-tests-skipped + + + skipTests + true + + + + true + + + + + skip-cli-install-when-maven-test-skip + + + maven.test.skip + true + + + + true + + + + + debug + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.basedir}/src/test/resources/logging-debug.properties + + + + + + + + + update-schemas-from-npm-artifact + + + + org.codehaus.mojo + exec-maven-plugin + + + update-copilot-schema-version + generate-sources + + exec + + + npm + ${project.parent.basedir}/scripts/codegen + + install + @github/copilot@${copilot.schema.version} + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + require-schema-version + validate + + enforce + + + + + copilot.schema.version + You must specify -Dcopilot.schema.version=VERSION (e.g. 1.0.25) + + + + + + + + + + + + codegen + + + + org.codehaus.mojo + exec-maven-plugin + + + codegen-npm-install + generate-sources + + exec + + + npm + ${project.parent.basedir}/scripts/codegen + + ci + + + + + codegen-generate + generate-sources + + exec + + + npm + ${project.parent.basedir}/scripts/codegen + + run + generate + + + + + + + + + + diff --git a/java/src/generated/java/com/github/copilot/generated/AbortEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AbortEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AbortEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AbortEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AbortReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/AbortReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AbortReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AbortReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantIdleEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantIdleEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantIdleEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantIdleEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantIntentEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantIntentEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantIntentEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantIntentEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageDeltaEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageDeltaEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageDeltaEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageDeltaEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageServerTools.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageServerTools.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageServerTools.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageServerTools.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequestType.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequestType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequestType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageToolRequestType.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantReasoningDeltaEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantReasoningDeltaEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantReasoningDeltaEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantReasoningDeltaEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantReasoningEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantReasoningEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantReasoningEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantReasoningEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantServerToolProgressEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantStreamingDeltaEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantStreamingDeltaEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantStreamingDeltaEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantStreamingDeltaEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantToolCallDeltaEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantToolCallDeltaEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantToolCallDeltaEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantToolCallDeltaEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantTurnEndEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnEndEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantTurnEndEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnEndEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantTurnRetryEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnRetryEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantTurnRetryEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnRetryEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantTurnStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantTurnStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantTurnStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageApiEndpoint.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageApiEndpoint.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantUsageApiEndpoint.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageApiEndpoint.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsage.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsageTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsageTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsageTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageCopilotUsageTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AssistantUsageQuotaSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageQuotaSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AssistantUsageQuotaSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageQuotaSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutoModeResolvedReasoningBucket.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutoModeResolvedReasoningBucket.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutoModeResolvedReasoningBucket.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutoModeResolvedReasoningBucket.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutoModeSwitchCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutoModeSwitchCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutoModeSwitchRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutoModeSwitchRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutoModeSwitchResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutoModeSwitchResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutoModeSwitchResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedOperation.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedOperation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedOperation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedOperation.java diff --git a/java/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/AutopilotObjectiveChangedStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/BinaryAssetType.java b/java/sdk/src/generated/java/com/github/copilot/generated/BinaryAssetType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/BinaryAssetType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/BinaryAssetType.java diff --git a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java b/java/sdk/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvas.java diff --git a/java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CanvasRegistryChangedCanvasAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/CapabilitiesChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CapabilitiesChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CapabilitiesChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CapabilitiesChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/CapabilitiesChangedUI.java b/java/sdk/src/generated/java/com/github/copilot/generated/CapabilitiesChangedUI.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CapabilitiesChangedUI.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CapabilitiesChangedUI.java diff --git a/java/src/generated/java/com/github/copilot/generated/CitableSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/CitableSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CitableSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CitableSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/CitationProvider.java b/java/sdk/src/generated/java/com/github/copilot/generated/CitationProvider.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CitationProvider.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CitationProvider.java diff --git a/java/src/generated/java/com/github/copilot/generated/CitationReference.java b/java/sdk/src/generated/java/com/github/copilot/generated/CitationReference.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CitationReference.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CitationReference.java diff --git a/java/src/generated/java/com/github/copilot/generated/CitationSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/CitationSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CitationSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CitationSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/CitationSpan.java b/java/sdk/src/generated/java/com/github/copilot/generated/CitationSpan.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CitationSpan.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CitationSpan.java diff --git a/java/src/generated/java/com/github/copilot/generated/Citations.java b/java/sdk/src/generated/java/com/github/copilot/generated/Citations.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/Citations.java rename to java/sdk/src/generated/java/com/github/copilot/generated/Citations.java diff --git a/java/src/generated/java/com/github/copilot/generated/CommandCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CommandCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CommandCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CommandCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/CommandExecuteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CommandExecuteEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CommandExecuteEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CommandExecuteEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/CommandQueuedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CommandQueuedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CommandQueuedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CommandQueuedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/CommandsChangedCommand.java b/java/sdk/src/generated/java/com/github/copilot/generated/CommandsChangedCommand.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CommandsChangedCommand.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CommandsChangedCommand.java diff --git a/java/src/generated/java/com/github/copilot/generated/CommandsChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CommandsChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CommandsChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CommandsChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsed.java diff --git a/java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsage.java diff --git a/java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/CompactionTrigger.java b/java/sdk/src/generated/java/com/github/copilot/generated/CompactionTrigger.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CompactionTrigger.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CompactionTrigger.java diff --git a/java/src/generated/java/com/github/copilot/generated/ContextTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/ContextTier.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ContextTier.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ContextTier.java diff --git a/java/src/generated/java/com/github/copilot/generated/CustomAgentsUpdatedAgent.java b/java/sdk/src/generated/java/com/github/copilot/generated/CustomAgentsUpdatedAgent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/CustomAgentsUpdatedAgent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/CustomAgentsUpdatedAgent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ElicitationCompletedAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/ElicitationCompletedAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ElicitationCompletedAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ElicitationCompletedAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/ElicitationCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ElicitationCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ElicitationCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ElicitationCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ElicitationRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ElicitationRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ElicitationRequestedMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ElicitationRequestedMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/ElicitationRequestedSchema.java b/java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedSchema.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ElicitationRequestedSchema.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ElicitationRequestedSchema.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExitPlanModeAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExitPlanModeAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExitPlanModeCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExitPlanModeCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExitPlanModeRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExitPlanModeRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExitPlanModeRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtension.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtension.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtension.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtension.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExtensionsLoadedExtensionStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExternalToolCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExternalToolCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExternalToolCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExternalToolCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ExternalToolRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ExternalToolRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ExternalToolRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ExternalToolRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/FactoryRunUpdatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/FactoryRunUpdatedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/FactoryRunUpdatedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/FactoryRunUpdatedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/GitHubMcpToolConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/HandoffRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/HandoffRepository.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HandoffRepository.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HandoffRepository.java diff --git a/java/src/generated/java/com/github/copilot/generated/HandoffSourceType.java b/java/sdk/src/generated/java/com/github/copilot/generated/HandoffSourceType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HandoffSourceType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HandoffSourceType.java diff --git a/java/src/generated/java/com/github/copilot/generated/HeaderEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/HeaderEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HeaderEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HeaderEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/HookEndError.java b/java/sdk/src/generated/java/com/github/copilot/generated/HookEndError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HookEndError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HookEndError.java diff --git a/java/src/generated/java/com/github/copilot/generated/HookEndEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/HookEndEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HookEndEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HookEndEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/HookProgressEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HookProgressEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HookProgressEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/HookStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/HookStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/HookStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HookStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java diff --git a/java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsResolvedSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteError.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteError.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMeta.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMeta.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMeta.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMetaUI.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMetaUI.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMetaUI.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpAppToolCallCompleteToolMetaUI.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedOutcome.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedOutcome.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshCompletedOutcome.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpHeadersRefreshRequiredReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthCompletionOutcome.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthHttpResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthRequestReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequestReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthRequestReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequestReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequiredEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthRequiredStaticClientConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequiredStaticClientConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthRequiredStaticClientConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthRequiredStaticClientConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpOauthWWWAuthenticateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpPromptsListChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpResourcesListChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpServerSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpServerSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpServerStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpServerStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpServerStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpServerStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpServerTransport.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpServerTransport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpServerTransport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpServerTransport.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpServersLoadedServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/McpToolsListChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureBadRequestKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallFailureTransport.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureTransport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallFailureTransport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureTransport.java diff --git a/java/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/PendingMessagesModifiedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PendingMessagesModifiedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/PendingMessagesModifiedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/PendingMessagesModifiedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/PermissionAllowAllMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionAllowAllMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/PermissionAllowAllMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/PermissionAllowAllMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/PermissionRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/PlanChangedOperation.java b/java/sdk/src/generated/java/com/github/copilot/generated/PlanChangedOperation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/PlanChangedOperation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/PlanChangedOperation.java diff --git a/java/src/generated/java/com/github/copilot/generated/ReasoningSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/ReasoningSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ReasoningSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ReasoningSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/SamplingCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SamplingCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SamplingCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SamplingCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SamplingRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SamplingRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SamplingRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SamplingRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ScheduleOrigin.java b/java/sdk/src/generated/java/com/github/copilot/generated/ScheduleOrigin.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ScheduleOrigin.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ScheduleOrigin.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionAutopilotObjectiveChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutopilotObjectiveChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionAutopilotObjectiveChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionAutopilotObjectiveChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionBackgroundTasksChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionBackgroundTasksChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionBackgroundTasksChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionBackgroundTasksChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionCompleteEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCompactionStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCompactionStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCompactionStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionContextChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionContextChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionContextChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionContextChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionContextClearedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCustomAgentsUpdatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCustomAgentsUpdatedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCustomAgentsUpdatedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCustomAgentsUpdatedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionCustomNotificationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCustomNotificationEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionCustomNotificationEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionCustomNotificationEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionErrorEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionExtensionsAttachmentsPushedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionExtensionsAttachmentsPushedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionExtensionsAttachmentsPushedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionExtensionsAttachmentsPushedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionExtensionsLoadedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionExtensionsLoadedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionExtensionsLoadedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionExtensionsLoadedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionHandoffEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionHandoffEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionHandoffEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionHandoffEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionIdleEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionIdleEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionIdleEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionIdleEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionInfoEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionInfoEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionInfoEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionInfoEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionLimitsConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionLimitsConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponseAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponseAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponseAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionLimitsExhaustedResponseAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionMcpServerStatusChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionMcpServersLoadedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionMcpServersLoadedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionMcpServersLoadedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionMcpServersLoadedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionModeChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionModeChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionModeChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionModeChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionPlanChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPlanChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionPlanChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionPlanChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionRemoteSteerableChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionRemoteSteerableChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionRemoteSteerableChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionRemoteSteerableChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionScheduleCancelledEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleCancelledEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionScheduleCancelledEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleCancelledEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleCreatedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionScheduleRearmedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleRearmedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionScheduleRearmedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionScheduleRearmedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionSessionLimitsChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionSessionLimitsChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionSessionLimitsChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionSessionLimitsChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionSkillsLoadedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionSkillsLoadedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionSkillsLoadedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionSkillsLoadedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionSnapshotRewindEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionSnapshotRewindEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionSnapshotRewindEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionSnapshotRewindEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionTaskCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionTaskCompleteEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionTaskCompleteEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionTaskCompleteEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionTitleChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionTitleChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionTitleChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionTitleChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionTodosChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionTodosChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionTodosChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionTodosChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionToolsUpdatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionToolsUpdatedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionToolsUpdatedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionToolsUpdatedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionTruncationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionTruncationEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionTruncationEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionTruncationEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionUsageInfoEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageInfoEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionUsageInfoEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageInfoEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionWarningEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionWarningEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionWarningEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionWarningEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SessionWorkspaceFileChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionWorkspaceFileChangedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SessionWorkspaceFileChangedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SessionWorkspaceFileChangedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownCodeChanges.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownCodeChanges.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownCodeChanges.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownCodeChanges.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetric.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownModelMetric.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetric.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricRequests.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricRequests.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricRequests.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricRequests.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricUsage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownModelMetricUsage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownModelMetricUsage.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/ShutdownType.java b/java/sdk/src/generated/java/com/github/copilot/generated/ShutdownType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ShutdownType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ShutdownType.java diff --git a/java/src/generated/java/com/github/copilot/generated/SkillInvokedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SkillInvokedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SkillInvokedTrigger.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedTrigger.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SkillInvokedTrigger.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedTrigger.java diff --git a/java/src/generated/java/com/github/copilot/generated/SkillSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SkillSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SkillSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SkillsLoadedSkill.java diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SubagentCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentDeselectedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentDeselectedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SubagentDeselectedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SubagentDeselectedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentFailedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentFailedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SubagentFailedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SubagentFailedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentSelectedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentSelectedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SubagentSelectedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SubagentSelectedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SubagentStartedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SystemMessageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SystemMessageEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/SystemMessageMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageMetadata.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SystemMessageMetadata.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageMetadata.java diff --git a/java/src/generated/java/com/github/copilot/generated/SystemMessageRole.java b/java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageRole.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SystemMessageRole.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SystemMessageRole.java diff --git a/java/src/generated/java/com/github/copilot/generated/SystemNotificationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SystemNotificationEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/SystemNotificationEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/SystemNotificationEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/TaskCompletionOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/TaskCompletionOutcome.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/TaskCompletionOutcome.java rename to java/sdk/src/generated/java/com/github/copilot/generated/TaskCompletionOutcome.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteError.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteError.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescription.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescription.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescription.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescription.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMeta.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMeta.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMeta.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUI.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUI.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUI.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUI.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUIVisibility.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUIVisibility.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUIVisibility.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteToolDescriptionMetaUIVisibility.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResource.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResource.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMeta.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMeta.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMeta.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUI.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUI.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUI.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUI.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUICsp.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUICsp.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUICsp.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUICsp.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissions.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissions.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsCamera.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartShellToolInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescription.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescription.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescription.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescription.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMeta.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMeta.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMeta.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUI.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUI.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUI.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUI.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUIVisibility.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUIVisibility.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUIVisibility.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartToolDescriptionMetaUIVisibility.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolSearchActivatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolSearchActivatedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolSearchActivatedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolSearchActivatedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/ToolUserRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolUserRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/ToolUserRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/ToolUserRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/UnknownSessionEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/UnknownSessionEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UnknownSessionEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UnknownSessionEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/UsageCheckpointModelCacheState.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageCheckpointModelCacheState.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UsageCheckpointModelCacheState.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UsageCheckpointModelCacheState.java diff --git a/java/src/generated/java/com/github/copilot/generated/UserInputCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/UserInputCompletedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UserInputCompletedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UserInputCompletedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/UserInputRequestedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/UserInputRequestedEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UserInputRequestedEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UserInputRequestedEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/UserMessageAgentMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/UserMessageAgentMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UserMessageAgentMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UserMessageAgentMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/UserMessageDelivery.java b/java/sdk/src/generated/java/com/github/copilot/generated/UserMessageDelivery.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UserMessageDelivery.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UserMessageDelivery.java diff --git a/java/src/generated/java/com/github/copilot/generated/UserMessageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/UserMessageEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/UserMessageEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/UserMessageEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/Verbosity.java b/java/sdk/src/generated/java/com/github/copilot/generated/Verbosity.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/Verbosity.java rename to java/sdk/src/generated/java/com/github/copilot/generated/Verbosity.java diff --git a/java/src/generated/java/com/github/copilot/generated/WorkingDirectoryContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkingDirectoryContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/WorkingDirectoryContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/WorkingDirectoryContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/WorkingDirectoryContextHostType.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkingDirectoryContextHostType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/WorkingDirectoryContextHostType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/WorkingDirectoryContextHostType.java diff --git a/java/src/generated/java/com/github/copilot/generated/WorkspaceFileChangedOperation.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkspaceFileChangedOperation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/WorkspaceFileChangedOperation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/WorkspaceFileChangedOperation.java diff --git a/java/src/generated/java/com/github/copilot/generated/package-info.java b/java/sdk/src/generated/java/com/github/copilot/generated/package-info.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/package-info.java rename to java/sdk/src/generated/java/com/github/copilot/generated/package-info.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AbortReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountAllUsers.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountAllUsers.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountAllUsers.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountAllUsers.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetCurrentAuthResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountGetQuotaResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLoginParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLoginResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountLogoutResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AccountQuotaSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountQuotaSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AccountQuotaSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountQuotaSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AdaptiveThinkingSupport.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AdaptiveThinkingSupport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AdaptiveThinkingSupport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AdaptiveThinkingSupport.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPath.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPath.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPath.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPath.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPathScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPathScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPathScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentDiscoveryPathScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentInfoSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfoSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentInfoSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentInfoSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryAttentionKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryAttentionKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryAttentionKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryAttentionKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryLastTerminalEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryLastTerminalEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryLastTerminalEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryLastTerminalEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLiveTargetEntryStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCapture.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCapture.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCapture.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCapture.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCaptureOpenErrorReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCaptureOpenErrorReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCaptureOpenErrorReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistryLogCaptureOpenErrorReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnError.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnPermissionMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnPermissionMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnPermissionMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnPermissionMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnRegistryTimeout.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnRegistryTimeout.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnRegistryTimeout.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnRegistryTimeout.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnSpawned.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnSpawned.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnSpawned.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnSpawned.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationError.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorField.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorField.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorField.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorField.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentRegistrySpawnValidationErrorReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsDiscoverResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetDiscoveryPathsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/BuiltInModelCatalogEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/BuiltInModelCatalogEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/BuiltInModelCatalogEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/BuiltInModelCatalogEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasActionInvokeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasActionInvokeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasActionInvokeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasActionInvokeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasCloseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasCloseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasCloseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasCloseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContextCapabilities.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContextCapabilities.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContextCapabilities.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasHostContextCapabilities.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasOpenResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CanvasSessionContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasSessionContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CanvasSessionContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CanvasSessionContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CapiSessionOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CapiSessionOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CapiSessionOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CapiSessionOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CommandsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CommandsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadata.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadata.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadata.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataRepository.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataRepository.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectedRemoteSessionMetadataRepository.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionPathCheck.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionPathCheck.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionPathCheck.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContentExclusionPathCheck.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ContextHeaviestMessage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContextHeaviestMessage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ContextHeaviestMessage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContextHeaviestMessage.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ContextTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContextTier.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ContextTier.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ContextTier.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/CurrentToolMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentToolMetadata.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/CurrentToolMetadata.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentToolMetadata.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsCollectedEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsCollectedEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsCollectedEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsCollectedEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntryKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntryKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntryKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsEntryKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsInclude.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsInclude.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsInclude.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsInclude.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsRedaction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsRedaction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsRedaction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsRedaction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsResultKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsResultKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsResultKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsResultKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSkippedEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSkippedEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSkippedEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSkippedEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DebugCollectLogsSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DisableBypassPermissionsMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredCanvas.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredCanvas.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredCanvas.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredCanvas.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtension.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionPlugin.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredExtensionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServerType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServerType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServerType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredMcpServerType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/EventsAgentScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsAgentScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/EventsAgentScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsAgentScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsCursorStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/EventsReadDirection.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Extension.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Extension.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Extension.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Extension.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProfile.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionLaunchProviderResolveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsDiscoverResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ExtensionsEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAbortParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAbortParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryAbortParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAbortParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryAgentSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryCurrentPhase.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryCurrentPhase.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryCurrentPhase.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryCurrentPhase.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryDeclaredLimits.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryDeclaredLimits.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryDeclaredLimits.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryDeclaredLimits.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryExecuteResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLine.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLine.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLine.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLine.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLineKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLineKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLineKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryLogLineKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseObservation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseObservation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseObservation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseObservation.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryPhaseStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressLine.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressLine.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressLine.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressLine.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressPage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressPage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressPage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryProgressPage.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunConsumed.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunConsumed.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunConsumed.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunConsumed.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunLimits.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunLimits.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunLimits.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunLimits.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunTerminal.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunTerminal.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/FactoryRunTerminal.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/FactoryRunTerminal.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryClientInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryClientInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryClientInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryClientInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryEvent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryEvent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryEvent.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryNotification.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryNotification.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryNotification.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubTelemetryNotification.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryCompactContextWindow.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryCompactContextWindow.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryCompactContextWindow.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryCompactContextWindow.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryFileRestoreSkipReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryFileRestoreSkipReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryFileRestoreSkipReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryFileRestoreSkipReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindChangeType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindChangeType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindChangeType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindChangeType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindFilePreview.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindFilePreview.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindFilePreview.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindFilePreview.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindOutcome.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindOutcome.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindOutcome.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindPoint.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindPoint.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindPoint.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindPoint.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindUnavailableReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindUnavailableReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindUnavailableReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistoryRewindUnavailableReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HistorySkippedFileRestore.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistorySkippedFileRestore.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HistorySkippedFileRestore.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HistorySkippedFileRestore.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HookType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HookType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstalledPlugin.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstalledPluginInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstalledPluginInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstalledPluginInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstalledPluginInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPath.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPath.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPath.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPath.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathLocation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathLocation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathLocation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionDiscoveryPathLocation.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceLocation.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionSourceType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsDiscoverResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/InstructionsGetDiscoveryPathsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestChunkResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartTransport.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartTransport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartTransport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpRequestStartTransport.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkError.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseChunkResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceHttpResponseStartResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LlmInferenceSetProviderResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/LocalSessionMetadataValue.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LocalSessionMetadataValue.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/LocalSessionMetadataValue.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/LocalSessionMetadataValue.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsReadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MarketplaceInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplaceInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MarketplaceInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplaceInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MarketplacePluginInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplacePluginInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MarketplacePluginInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplacePluginInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MarketplaceRefreshEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplaceRefreshEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MarketplaceRefreshEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MarketplaceRefreshEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseCapability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseCapability.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseCapability.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseCapability.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsDiagnoseServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetails.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetails.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetails.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetails.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsAvailableDisplayMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsAvailableDisplayMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsAvailableDisplayMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsAvailableDisplayMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsDisplayMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsDisplayMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsDisplayMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsDisplayMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsPlatform.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsPlatform.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsPlatform.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsPlatform.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsTheme.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsTheme.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsTheme.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsHostContextDetailsTheme.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsResourceContent.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsResourceContent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsResourceContent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsResourceContent.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetails.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetails.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetails.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetails.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsAvailableDisplayMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsAvailableDisplayMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsAvailableDisplayMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsAvailableDisplayMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsDisplayMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsDisplayMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsDisplayMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsDisplayMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsPlatform.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsPlatform.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsPlatform.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsPlatform.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsTheme.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsTheme.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsTheme.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAppsSetHostContextDetailsTheme.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigAddParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigRemoveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigUpdateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpDiscoverResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpExecuteSamplingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpHostState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpHostState.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpHostState.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpHostState.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpOauthLoginGrantType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpOauthLoginGrantType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpOauthLoginGrantType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpOauthLoginGrantType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpResource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpResource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpResourceAnnotations.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceAnnotations.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpResourceAnnotations.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceAnnotations.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpResourceContent.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceContent.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpResourceContent.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceContent.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpResourceIcon.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceIcon.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpResourceIcon.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceIcon.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpResourceTemplate.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceTemplate.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpResourceTemplate.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpResourceTemplate.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpSamplingExecutionAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpSamplingExecutionAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpSamplingExecutionAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpSamplingExecutionAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpServer.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerFailureInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerFailureInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpServerFailureInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerFailureInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerNeedsAuthInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerNeedsAuthInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpServerNeedsAuthInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerNeedsAuthInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpSetEnvValueModeDetails.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpSetEnvValueModeDetails.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpSetEnvValueModeDetails.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpSetEnvValueModeDetails.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpToolUi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpToolUiVisibility.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpTools.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/McpTools.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpTools.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MemoryConfiguration.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MemoryConfiguration.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MemoryConfiguration.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MemoryConfiguration.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotCurrentMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotCurrentMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotCurrentMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotCurrentMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadata.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadata.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadata.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataRepository.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataRepository.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataRepository.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataTaskType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataTaskType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataTaskType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/MetadataSnapshotRemoteMetadataTaskType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Model.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Model.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBilling.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBilling.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelBilling.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBilling.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingPromo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingPromo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingPromo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingPromo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPrices.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelBillingTokenPricesLongContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilities.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilities.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilities.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilities.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimits.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimits.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimits.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimits.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimitsVision.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimitsVision.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimitsVision.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesLimitsVision.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverride.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverride.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverride.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverride.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimits.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimits.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimits.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimits.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimitsVision.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimitsVision.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimitsVision.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideLimitsVision.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideSupports.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideSupports.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideSupports.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesOverrideSupports.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelPickerCategory.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPickerCategory.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelPickerCategory.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPickerCategory.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelPickerPriceCategory.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPickerPriceCategory.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelPickerPriceCategory.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPickerPriceCategory.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPolicy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelPolicy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPolicy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelPolicyState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPolicyState.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelPolicyState.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelPolicyState.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelsGetBuiltInCatalogResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsGetBuiltInCatalogResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelsGetBuiltInCatalogResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsGetBuiltInCatalogResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OpenCanvasInstance.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OpenCanvasInstance.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OpenCanvasInstance.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OpenCanvasInstance.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRule.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRule.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRule.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRule.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRuleSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRuleSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRuleSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyRuleSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateAdditionalContentExclusionPolicyScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateContextTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateContextTier.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateContextTier.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateContextTier.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateEnvValueMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateEnvValueMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateEnvValueMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateEnvValueMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateReasoningSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateReasoningSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateReasoningSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateReasoningSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateToolFilterPrecedence.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateToolFilterPrecedence.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateToolFilterPrecedence.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/OptionsUpdateToolFilterPrecedence.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PendingPermissionRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PendingPermissionRequest.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PendingPermissionRequest.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PendingPermissionRequest.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionOutcome.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionDecisionSurface.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionLocationType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionLocationType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionLocationType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionLocationType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionPathsConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionPathsConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionPathsConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionPathsConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionRule.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRule.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionRule.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRule.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionRulesSet.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRulesSet.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionRulesSet.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionRulesSet.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionUrlsConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionUrlsConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionUrlsConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionUrlsConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsAllowAllMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsAllowAllMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsAllowAllMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsAllowAllMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRule.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRule.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRule.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRule.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsConfigureAdditionalContentExclusionPolicyScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsModifyRulesScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsModifyRulesScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsModifyRulesScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsModifyRulesScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetAllowAllSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetAllowAllSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetAllowAllSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetAllowAllSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetApproveAllSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetApproveAllSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetApproveAllSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PermissionsSetApproveAllSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodoDependency.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodoDependency.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodoDependency.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodoDependency.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodosRow.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodosRow.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodosRow.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PlanSqlTodosRow.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Plugin.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Plugin.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Plugin.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Plugin.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginUpdateAllEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginUpdateAllEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginUpdateAllEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginUpdateAllEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsInstallResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesAddResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesBrowseResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRefreshResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsMarketplacesRemoveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsUninstallParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUninstallParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsUninstallParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUninstallParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateAllResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateAllResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateAllResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateAllResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/PluginsUpdateResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigAzure.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigAzure.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigAzure.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigAzure.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigTransport.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigTransport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigTransport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigTransport.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigWireApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigWireApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigWireApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigWireApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointTransport.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointTransport.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointTransport.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointTransport.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointWireApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointWireApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointWireApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderEndpointWireApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderModelConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderModelConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderModelConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderModelConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderSessionToken.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderSessionToken.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderSessionToken.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderSessionToken.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderTokenGetTokenResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/QueueInsertMessage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueInsertMessage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/QueueInsertMessage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueInsertMessage.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItems.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItems.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItems.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItems.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItemsKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItemsKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItemsKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueuePendingItemsKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ReasoningSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ReasoningSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ReasoningSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ReasoningSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfigExistingMcSession.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfigExistingMcSession.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfigExistingMcSession.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteControlConfigExistingMcSession.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataRepository.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataRepository.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataRepository.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataTaskType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataTaskType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataTaskType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataTaskType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataValue.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataValue.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataValue.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMetadataValue.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionRepository.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionRepository.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RemoteSessionRepository.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RpcCaller.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RpcCaller.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RpcCaller.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RpcCaller.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RpcMapper.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RpcMapper.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RpcMapper.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RpcMapper.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/RunOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/RunOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/RunOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/RunOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigAuth.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimental.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimental.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimental.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimental.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimentalSeatbelt.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimentalSeatbelt.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimentalSeatbelt.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyExperimentalSeatbelt.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyFilesystem.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyFilesystem.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyFilesystem.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyFilesystem.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetwork.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetwork.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetwork.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetwork.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetworkProxy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetworkProxy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetworkProxy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicyNetworkProxy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicySeatbelt.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicySeatbelt.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicySeatbelt.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxConfigUserPolicySeatbelt.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SecretsAddFilterValuesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SendAgentMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendAgentMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SendAgentMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendAgentMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SendMessageItem.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendMessageItem.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SendMessageItem.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendMessageItem.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SendMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SendMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SendMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkill.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerUserApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAbortParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAbortParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAbortParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAbortParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAbortResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAbortResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAbortResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAbortResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentDeselectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentDeselectParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentDeselectParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentDeselectParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentGetCurrentResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentReloadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSelectResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentSetPromptParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCancelAllBackgroundAgentsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCancelAllBackgroundAgentsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCancelAllBackgroundAgentsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCancelAllBackgroundAgentsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionInvokeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasCloseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasCloseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasCloseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasCloseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListOpenResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasOpenResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCapability.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsEnqueueResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsExecuteResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsHandlePendingCommandResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsInvokeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsInvokeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsInvokeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsInvokeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionItem.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionItem.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionItem.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionItem.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsGetTriggerCharactersResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsRequestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionCheckPathsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionContextHostType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContextHostType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionContextHostType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContextHostType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugCollectLogsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogRegisterInterestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogReleaseInterestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogTailResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReloadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReloadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReloadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReloadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsSendAttachmentsToMessageParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsSendAttachmentsToMessageParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsSendAttachmentsToMessageParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsSendAttachmentsToMessageParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryAgentResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryCancelResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunDetailResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunProgressResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryGetRunResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalGetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalPutParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalPutParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalPutParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryJournalPutParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryListRunsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryLogParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryLogParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryLogParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryLogParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryResumeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFactoryRunResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetStartResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsAppendFileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsAppendFileParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsAppendFileParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsAppendFileParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsErrorCode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsErrorCode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsErrorCode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsErrorCode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsExistsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsMkdirParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsMkdirParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsMkdirParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsMkdirParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntryType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntryType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntryType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesEntryType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReaddirWithTypesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsRenameParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsRenameParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsRenameParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsRenameParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsRmParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsRmParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsRmParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsRmParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderConventions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderConventions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderConventions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderConventions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteExistsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteQueryType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionError.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionError.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionError.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionErrorClass.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionErrorClass.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionErrorClass.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionErrorClass.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionStatement.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionStatement.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionStatement.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSqliteTransactionStatement.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsStatResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetStatusResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSetCredentialsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryAbortManualCompactionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCancelBackgroundCompactionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryClearContextResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryCompactResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryListRewindPointsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryPreviewRewindResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryRewindResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistorySummarizeForHandoffResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryTruncateResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstalledPlugin.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsGetSourcesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInterruptMainTurnResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionBaselineData.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionBaselineData.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionBaselineData.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionBaselineData.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionClientType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionClientType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionClientType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionClientType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionDetails.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionDetails.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionDetails.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionDetails.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionPredictParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionPredictParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionPredictParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionPredictParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultAvailable.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultAvailable.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultAvailable.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultAvailable.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultUnavailable.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultUnavailable.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultUnavailable.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionResultUnavailable.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTier.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTier.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTier.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTierOption.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTierOption.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTierOption.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionTierOption.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionUnavailableReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionUnavailableReason.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionUnavailableReason.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionUnavailableReason.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitsConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitsConfig.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLimitsConfig.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitsConfig.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionListFilter.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionListFilter.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionListFilter.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionListFilter.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLogLevel.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogLevel.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLogLevel.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogLevel.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLogParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLogParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLogResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLogResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLogResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionLspInitializeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspInitializeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionLspInitializeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspInitializeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettings.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsCallToolParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsCallToolParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsCallToolParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsCallToolParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsDiagnoseResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsGetHostContextResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsListToolsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsReadResourceResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsSetHostContextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsSetHostContextParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsSetHostContextParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsSetHostContextParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpCancelSamplingExecutionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpExecuteSamplingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersHandlePendingHeadersRefreshRequestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpIsServerRunningResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListToolsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthAuthenticationStateChangedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthHandlePendingRequestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthRespondResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRemoveGitHubResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesListTemplatesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesReadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRestartServerParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRestartServerParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRestartServerParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRestartServerParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetEnvValueModeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStartServerParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStartServerParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStartServerParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStartServerParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStopServerParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStopServerParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStopServerParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpStopServerParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataActivityResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextHeaviestMessagesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataIsProcessingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecomputeContextTokensResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecordContextChangeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecordContextChangeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecordContextChangeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataRecordContextChangeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSetWorkingDirectoryResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataSnapshotResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModeGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeGetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModeGetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeGetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelPriceCategory.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSetReasoningEffortResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameGetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetAutoResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameSetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicy.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicy.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicy.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRule.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRule.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRule.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRule.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyRuleSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsAdditionalContentExclusionPolicyScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsEnvValueMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsEnvValueMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsEnvValueMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsEnvValueMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsReasoningSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsReasoningSummary.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsReasoningSummary.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOpenOptionsReasoningSummary.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsUpdateResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsConfigureResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustAddTrustedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustIsTrustedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsGetAllowAllResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsHandlePendingPermissionRequestResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsAddToolApprovalResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApplyResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsResolveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsModifyRulesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsNotifyPromptShownResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsAddResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinAllowedDirectoriesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsIsPathWithinWorkspaceResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsUpdatePrimaryResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPendingRequestsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsResetSessionApprovalsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetAllowAllResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetApproveAllResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsSetRequiredResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsSetUnrestrictedModeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanDeleteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanDeleteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanDeleteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanDeleteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanReadSqlTodosWithDependenciesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanUpdateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanUpdateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPlanUpdateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanUpdateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsReloadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsReloadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsReloadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsReloadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderAddResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderGetEndpointResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueClearParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueClearParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueClearParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueClearParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDuplicateAtResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueInsertAtResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueMoveItemResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueuePendingItemsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveAtResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueRemoveMostRecentResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSendNowResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSetDrainPausedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSetDrainPausedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSetDrainPausedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSetDrainPausedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueUpdateTextResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteEnableResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteNotifySteerableChangedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteNotifySteerableChangedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteNotifySteerableChangedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteNotifySteerableChangedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRpc.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleStopResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendMessagesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSendParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSendParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSendResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSendResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellCancelUserRequestedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecuteUserRequestedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellKillResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionShutdownParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShutdownParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionShutdownParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShutdownParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsDisableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsDisableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsDisableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsDisableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnsureLoadedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnsureLoadedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnsureLoadedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsEnsureLoadedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsGetInvokedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsReloadResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionSuspendParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSuspendParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionSuspendParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSuspendParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksCancelResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetCurrentPromotableResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksGetProgressResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteCurrentToBackgroundResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksPromoteToBackgroundResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRefreshParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRefreshParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRefreshParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRefreshParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksRemoveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksSendMessageResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksStartAgentResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksWaitForPendingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksWaitForPendingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTasksWaitForPendingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksWaitForPendingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryGetEngagementIdResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetrySetFeatureOverridesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetrySetFeatureOverridesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetrySetFeatureOverridesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetrySetFeatureOverridesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsGetCurrentMetadataResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsHandlePendingToolCallResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsInitializeAndValidateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsInitializeAndValidateParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsInitializeAndValidateParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsInitializeAndValidateParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsUpdateSubagentSettingsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsUpdateSubagentSettingsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionToolsUpdateSubagentSettingsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsUpdateSubagentSettingsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiElicitationResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiEphemeralQueryResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingAutoModeSwitchResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingElicitationResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingExitPlanModeResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSamplingResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingSessionLimitsExhaustedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandlePendingUserInputResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiRegisterDirectAutoModeSwitchHandlerResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiUnregisterDirectAutoModeSwitchHandlerResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityGetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilitySetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContext.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContext.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContext.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContext.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextHostType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextHostType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextHostType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextHostType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAddSummaryResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesAutopilotObjectiveExistsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesCreateFileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesCreateFileParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesCreateFileParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesCreateFileParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDeleteAutopilotObjectiveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesEnsureResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesGetWorkspaceResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListCheckpointsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesListFilesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadAutopilotObjectiveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadCheckpointResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesReadFileResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesSaveLargePasteResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesTruncateSummariesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesUpdateMetadataResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesWriteAutopilotObjectiveResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsBulkDeleteResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCheckInUseResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsCloseParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCloseParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsCloseParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCloseParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConnectResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsEnrichMetadataResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByPrefixResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsFindByTaskIdResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsForkParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsForkParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsForkParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsForkParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsForkResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsForkResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsForkResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsForkResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetLastForContextResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetRemoteControlStatusResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetRemoteControlStatusResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetRemoteControlStatusResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetRemoteControlStatusResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetSizesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetSizesResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsGetSizesResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetSizesResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadDeferredRepoHooksResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenAttach.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenAttach.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenAttach.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenAttach.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCloud.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCloud.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCloud.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCloud.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCreate.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCreate.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCreate.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenCreate.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoff.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoff.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoff.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoff.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoffTaskType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoffTaskType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoffTaskType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenHandoffTaskType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgress.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgress.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgress.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgress.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStep.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStep.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStep.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenProgressStep.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenRemote.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenRemote.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenRemote.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenRemote.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResume.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResume.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResume.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResume.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResumeLast.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResumeLast.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResumeLast.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenResumeLast.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenStatus.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenStatus.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsOpenStatus.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsPruneOldResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsRegisterExtensionToolsOnSessionResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsReleaseLockParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsReleaseLockParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsReleaseLockParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsReleaseLockParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsReloadPluginHooksParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsReloadPluginHooksParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsReloadPluginHooksParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsReloadPluginHooksParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsSaveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSaveParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsSaveParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSaveParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetAdditionalPluginsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetAdditionalPluginsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetAdditionalPluginsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetAdditionalPluginsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsSetRemoteControlSteeringResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStartRemoteControlResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsStopRemoteControlResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsTransferRemoteControlResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShellInitProfile.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitProfile.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShellInitProfile.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitProfile.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShellInitScript.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitScript.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShellInitScript.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitScript.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShellInitScriptShell.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitScriptShell.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShellInitScriptShell.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellInitScriptShell.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShellKillSignal.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellKillSignal.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShellKillSignal.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellKillSignal.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShellOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellOptions.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShellOptions.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShellOptions.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ShutdownType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShutdownType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ShutdownType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ShutdownType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Skill.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Skill.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Skill.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Skill.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryPath.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryPath.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryPath.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryPath.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryScope.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryScope.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillDiscoveryScope.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillSource.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillSource.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillSource.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsConfigSetDisabledSkillsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsDiscoverResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsGetDiscoveryPathsResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SkillsInvokedSkill.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsInvokedSkill.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SkillsInvokedSkill.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillsInvokedSkill.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandAgentPromptResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandAgentPromptResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandAgentPromptResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandAgentPromptResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandCompletedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandCompletedResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandCompletedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandCompletedResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInfo.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInfo.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInfo.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInput.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInput.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInput.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInput.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputChoice.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputChoice.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputChoice.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputChoice.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputCompletion.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputCompletion.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputCompletion.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInputCompletion.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInvocationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInvocationResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInvocationResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandInvocationResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandKind.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandKind.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandKind.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandOption.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandOption.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandOption.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandOption.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandSelectSubcommandResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandTextResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandTextResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SlashCommandTextResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SlashCommandTextResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntry.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntry.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntry.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntryContextTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntryContextTier.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntryContextTier.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SubagentSettingsEntryContextTier.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Tool.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Tool.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Tool.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Tool.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ToolsListParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ToolsListResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIAutoModeSwitchResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIAutoModeSwitchResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIAutoModeSwitchResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIAutoModeSwitchResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponseAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponseAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponseAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResponseAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationSchema.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationSchema.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIElicitationSchema.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationSchema.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIExitPlanModeResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingSamplingResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingSamplingResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingSamplingResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingSamplingResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponseAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponseAction.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponseAction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UISessionLimitsExhaustedResponseAction.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UIUserInputResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIUserInputResponse.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UIUserInputResponse.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIUserInputResponse.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsCodeChanges.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsCodeChanges.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsCodeChanges.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsCodeChanges.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricRequests.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricRequests.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricRequests.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricRequests.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricUsage.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricUsage.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetricUsage.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsTokenDetail.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsTokenDetail.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsTokenDetail.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingMetadata.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetParams.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/Verbosity.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Verbosity.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/Verbosity.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/Verbosity.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChangeType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChangeType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChangeType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChangeType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffMode.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffMode.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffMode.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceSummaryHostType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceSummaryHostType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspaceSummaryHostType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceSummaryHostType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspacesCheckpoints.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspacesCheckpoints.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspacesCheckpoints.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspacesCheckpoints.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/WorkspacesWorkspaceDetailsHostType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspacesWorkspaceDetailsHostType.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/WorkspacesWorkspaceDetailsHostType.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspacesWorkspaceDetailsHostType.java diff --git a/java/src/generated/java/com/github/copilot/generated/rpc/package-info.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/package-info.java similarity index 100% rename from java/src/generated/java/com/github/copilot/generated/rpc/package-info.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/package-info.java diff --git a/java/src/main/java/com/github/copilot/AllowCopilotExperimental.java b/java/sdk/src/main/java/com/github/copilot/AllowCopilotExperimental.java similarity index 100% rename from java/src/main/java/com/github/copilot/AllowCopilotExperimental.java rename to java/sdk/src/main/java/com/github/copilot/AllowCopilotExperimental.java diff --git a/java/src/main/java/com/github/copilot/CliServerManager.java b/java/sdk/src/main/java/com/github/copilot/CliServerManager.java similarity index 100% rename from java/src/main/java/com/github/copilot/CliServerManager.java rename to java/sdk/src/main/java/com/github/copilot/CliServerManager.java diff --git a/java/src/main/java/com/github/copilot/ConnectionState.java b/java/sdk/src/main/java/com/github/copilot/ConnectionState.java similarity index 100% rename from java/src/main/java/com/github/copilot/ConnectionState.java rename to java/sdk/src/main/java/com/github/copilot/ConnectionState.java diff --git a/java/src/main/java/com/github/copilot/CopilotClient.java b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java similarity index 81% rename from java/src/main/java/com/github/copilot/CopilotClient.java rename to java/sdk/src/main/java/com/github/copilot/CopilotClient.java index 44878b87e..3dba1ea66 100644 --- a/java/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -5,8 +5,11 @@ package com.github.copilot; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,8 +24,15 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.github.copilot.ffi.FfiRuntimeHost; +import com.github.copilot.ffi.NativeRuntimeLoader; import com.github.copilot.rpc.CopilotClientMode; import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.InProcessRuntimeConnection; +import com.github.copilot.rpc.RuntimeConnection; +import com.github.copilot.rpc.StdioRuntimeConnection; +import com.github.copilot.rpc.TcpRuntimeConnection; +import com.github.copilot.rpc.UriRuntimeConnection; import com.github.copilot.rpc.CreateSessionResponse; import com.github.copilot.generated.rpc.SessionOptionsUpdateParams; import com.github.copilot.generated.rpc.SessionInstalledPlugin; @@ -111,7 +121,9 @@ public final class CopilotClient implements AutoCloseable { private volatile boolean disposed = false; private final String optionsHost; private final Integer optionsPort; + private final RuntimeConnection runtimeConnection; private final String effectiveConnectionToken; + private final Runnable closeHook; private volatile List modelsCache; private final Object modelsCacheLock = new Object(); @@ -131,7 +143,30 @@ public CopilotClient() { * if mutually exclusive options are provided */ public CopilotClient(CopilotClientOptions options) { + this(options, null); + } + + CopilotClient(CopilotClientOptions options, Runnable closeHook) { this.options = options != null ? options : new CopilotClientOptions(); + this.closeHook = closeHook; + + // Resolve the transport: an explicit RuntimeConnection wins; otherwise the + // COPILOT_SDK_DEFAULT_CONNECTION env var, or the individual transport options. + RuntimeConnection requestedConnection = this.options.getConnection(); + if (requestedConnection != null) { + validateEnvironmentOptions(this.options, requestedConnection); + validateConnectionConflicts(this.options, requestedConnection); + applyConnection(this.options, requestedConnection); + } else { + requestedConnection = resolveDefaultConnection(this.options); + validateEnvironmentOptions(this.options, requestedConnection); + // When the env var overrides inference (e.g. inprocess), validate that + // no legacy transport options conflict with the resolved connection. + if (requestedConnection != null) { + validateConnectionConflicts(this.options, requestedConnection); + } + } + this.runtimeConnection = requestedConnection; // When cliUrl is set, auto-correct useStdio since we're connecting via TCP if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty()) { @@ -199,6 +234,275 @@ public CopilotClient(CopilotClientOptions options) { this.serverManager.setConnectionToken(this.effectiveConnectionToken); } + /** + * Environment variable that overrides the transport used when the caller does + * not set {@link CopilotClientOptions#setConnection(RuntimeConnection)}. + * Accepts {@code "inprocess"} or {@code "stdio"} (case-insensitive); unset + * keeps the transport selected by the individual transport options. Any other + * value is an error. Ignored when a connection is set explicitly. + */ + static final String DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION"; + + /** + * Resolves the connection to use when the caller did not set one, honoring + * {@link #DEFAULT_CONNECTION_ENV_VAR} and otherwise inferring the transport + * from the individual transport options. + */ + private static RuntimeConnection resolveDefaultConnection(CopilotClientOptions options) { + return resolveDefaultConnection(options, System.getenv(DEFAULT_CONNECTION_ENV_VAR)); + } + + /** + * Resolves the default connection from an explicit environment-variable value. + * Package-private so tests can supply the value directly. + */ + static RuntimeConnection resolveDefaultConnection(CopilotClientOptions options, String envValue) { + if (envValue != null && !envValue.isEmpty()) { + if ("inprocess".equalsIgnoreCase(envValue)) { + // Explicit subprocess options take precedence over the env var default. + if (options.getCliUrl() != null && !options.getCliUrl().isEmpty()) { + return inferConnectionFromOptions(options); + } + if (options.getCliPath() != null && !options.getCliPath().isEmpty()) { + return inferConnectionFromOptions(options); + } + if (options.getPort() != 0) { + return inferConnectionFromOptions(options); + } + if (!options.isUseStdio() || options.getTcpConnectionToken() != null) { + return inferConnectionFromOptions(options); + } + return RuntimeConnection.forInProcess(); + } + if (!"stdio".equalsIgnoreCase(envValue)) { + throw new IllegalArgumentException("Invalid " + DEFAULT_CONNECTION_ENV_VAR + " value '" + envValue + + "'. Expected 'inprocess', 'stdio', or unset."); + } + } + + return inferConnectionFromOptions(options); + } + + /** + * Maps the individual transport options onto the equivalent + * {@link RuntimeConnection}, preserving the behavior of clients written before + * connections existed. + */ + private static RuntimeConnection inferConnectionFromOptions(CopilotClientOptions options) { + String cliUrl = options.getCliUrl(); + List args = options.getCliArgs() != null ? Arrays.asList(options.getCliArgs()) : null; + if (cliUrl != null && !cliUrl.isEmpty()) { + return RuntimeConnection.forUri(cliUrl).setConnectionToken(options.getTcpConnectionToken()); + } + if (options.isUseStdio()) { + StdioRuntimeConnection stdio = RuntimeConnection.forStdio(options.getCliPath()); + if (args != null) { + stdio.setArgs(args); + } + return stdio; + } + TcpRuntimeConnection tcp = RuntimeConnection.forTcp().setPath(options.getCliPath()).setPort(options.getPort()) + .setConnectionToken(options.getTcpConnectionToken()); + if (args != null) { + tcp.setArgs(args); + } + return tcp; + } + + /** + * Rejects transport options that contradict the configured connection. Values + * that match what the connection implies are accepted so that constructing + * several clients from the same options instance stays valid. + */ + private static void validateConnectionConflicts(CopilotClientOptions options, RuntimeConnection connection) { + String impliedPath = null; + String impliedUrl = null; + String impliedToken = null; + int impliedPort = 0; + boolean impliedUseStdio = true; + List impliedArgs = null; + + if (connection instanceof StdioRuntimeConnection stdio) { + impliedPath = stdio.getPath(); + impliedArgs = stdio.getArgs(); + } else if (connection instanceof TcpRuntimeConnection tcp) { + impliedPath = tcp.getPath(); + impliedPort = tcp.getPort(); + impliedToken = tcp.getConnectionToken(); + impliedArgs = tcp.getArgs(); + impliedUseStdio = false; + } else if (connection instanceof UriRuntimeConnection uri) { + impliedUrl = uri.getUrl(); + impliedToken = uri.getConnectionToken(); + impliedUseStdio = false; + } + + rejectConflict("CliPath", options.getCliPath() != null && !options.getCliPath().equals(impliedPath)); + rejectConflict("CliUrl", options.getCliUrl() != null && !options.getCliUrl().isEmpty() + && !options.getCliUrl().equals(impliedUrl)); + rejectConflict("Port", options.getPort() != 0 && options.getPort() != impliedPort); + rejectConflict("TcpConnectionToken", + options.getTcpConnectionToken() != null && !options.getTcpConnectionToken().equals(impliedToken)); + rejectConflict("UseStdio", !options.isUseStdio() && impliedUseStdio); + rejectConflict("CliArgs", options.getCliArgs() != null + && !Arrays.asList(options.getCliArgs()).equals(impliedArgs == null ? List.of() : impliedArgs)); + } + + private static void rejectConflict(String optionName, boolean conflicting) { + if (conflicting) { + throw new IllegalArgumentException("CopilotClientOptions." + optionName + + " cannot be combined with CopilotClientOptions.setConnection(); configure the transport on the" + + " RuntimeConnection instead."); + } + } + + /** + * Projects the configured connection onto the individual transport options so + * that the rest of the client sees a single, consistent view of the transport. + */ + private static void applyConnection(CopilotClientOptions options, RuntimeConnection connection) { + if (connection instanceof StdioRuntimeConnection stdio) { + options.setUseStdio(true); + if (stdio.getPath() != null) { + options.setCliPath(stdio.getPath()); + } + applyConnectionArgs(options, stdio.getArgs()); + } else if (connection instanceof TcpRuntimeConnection tcp) { + options.setUseStdio(false); + if (tcp.getPath() != null) { + options.setCliPath(tcp.getPath()); + } + options.setPort(tcp.getPort()); + if (tcp.getConnectionToken() != null) { + options.setTcpConnectionToken(tcp.getConnectionToken()); + } + applyConnectionArgs(options, tcp.getArgs()); + } else if (connection instanceof UriRuntimeConnection uri) { + options.setUseStdio(false); + options.setCliUrl(uri.getUrl()); + if (uri.getConnectionToken() != null) { + options.setTcpConnectionToken(uri.getConnectionToken()); + } + } + } + + private static void applyConnectionArgs(CopilotClientOptions options, List args) { + if (args != null) { + options.setCliArgs(args.toArray(new String[0])); + } + } + + /** + * Rejects per-process options that the in-process transport cannot honor. These + * options are lowered onto a child process, but the in-process runtime runs + * inside the shared host process, whose single environment and working + * directory cannot carry per-client values. + */ + private static void validateEnvironmentOptions(CopilotClientOptions options, RuntimeConnection connection) { + if (!(connection instanceof InProcessRuntimeConnection)) { + return; + } + + rejectInProcessOption("Environment", options.getEnvironment() != null && !options.getEnvironment().isEmpty(), + "set the variables on the host process environment instead"); + rejectInProcessOption("Telemetry", options.getTelemetry() != null, + "configure telemetry through the host process environment instead"); + rejectInProcessOption("Cwd", options.getCwd() != null, + "set the process working directory before creating the client instead"); + rejectInProcessOption("CliArgs", options.getCliArgs() != null && options.getCliArgs().length > 0, + "use the typed client options instead"); + } + + private static void rejectInProcessOption(String optionName, boolean present, String remedy) { + if (present) { + throw new IllegalArgumentException("CopilotClientOptions." + optionName + + " is not supported with RuntimeConnection.forInProcess(): the in-process runtime shares the host" + + " process, so per-client values cannot be honored; " + remedy + "."); + } + } + + /** + * Duplex streams of an in-process runtime, together with the resource that owns + * its lifetime. + * + * @param receiveStream + * stream carrying messages from the runtime + * @param sendStream + * stream carrying messages to the runtime + * @param host + * resource closed when the client stops + */ + record InProcessTransport(InputStream receiveStream, OutputStream sendStream, AutoCloseable host) { + } + + /** + * Opens the transport for the in-process runtime. Package-private so tests can + * substitute a fake for the native runtime. + */ + @FunctionalInterface + interface InProcessTransportFactory { + /** + * Opens the in-process transport. + * + * @param options + * client options used to configure the runtime + * @return the opened transport + * @throws IOException + * if the runtime cannot be started + */ + InProcessTransport open(CopilotClientOptions options) throws IOException; + } + + private volatile InProcessTransportFactory inProcessTransportFactory = CopilotClient::openInProcessTransport; + + /** + * Returns the resolved connection describing how this client reaches the + * runtime. Package-private test seam. + * + * @return the resolved connection + */ + RuntimeConnection getRuntimeConnection() { + return runtimeConnection; + } + + /** + * Replaces the in-process transport factory. Package-private test seam. + * + * @param factory + * the factory to use + */ + void setInProcessTransportFactory(InProcessTransportFactory factory) { + this.inProcessTransportFactory = java.util.Objects.requireNonNull(factory, "factory must not be null"); + } + + private static InProcessTransport openInProcessTransport(CopilotClientOptions options) throws IOException { + FfiRuntimeHost host = new FfiRuntimeHost(); + try { + host.start(resolveInProcessEntrypoint(), options); + } catch (RuntimeException | Error e) { + host.close(); + throw e; + } + return new InProcessTransport(host.getReceiveStream(), host.getSendStream(), host); + } + + /** + * Resolves the runtime entrypoint handed to the in-process host. The copilot + * CLI executable is resolved from the same bundled location as + * {@code runtime.node} — no environment variables or PATH search. + */ + private static String resolveInProcessEntrypoint() throws IOException { + return NativeRuntimeLoader.resolveEntrypoint().toString(); + } + + private static void closeRuntimeHost(AutoCloseable host) { + try { + host.close(); + } catch (Exception e) { + LOG.log(Level.FINE, "Error closing in-process runtime host", e); + } + } + /** * Starts the Copilot client and connects to the server. * @@ -227,11 +531,16 @@ private CompletableFuture startCore() { private Connection startCoreBody() { Process process = null; + InProcessTransport inProcessTransport = null; long startNanos = System.nanoTime(); try { JsonRpcClient rpc; - if (optionsHost != null && optionsPort != null) { + if (runtimeConnection instanceof InProcessRuntimeConnection) { + // In-process runtime hosted in this process (no child process) + inProcessTransport = inProcessTransportFactory.open(options); + rpc = JsonRpcClient.fromStreams(inProcessTransport.receiveStream(), inProcessTransport.sendStream()); + } else if (optionsHost != null && optionsPort != null) { // External server (TCP) rpc = serverManager.connectToServer(null, optionsHost, optionsPort); } else { @@ -245,7 +554,8 @@ private Connection startCoreBody() { LoggingHelpers.logTiming(LOG, Level.FINE, "CopilotClient.start transport setup complete. Elapsed={Elapsed}", startNanos); - Connection connection = new Connection(rpc, process, new ServerRpc(rpc::invoke)); + Connection connection = new Connection(rpc, process, new ServerRpc(rpc::invoke), + inProcessTransport == null ? null : inProcessTransport.host()); // Register handlers for server-to-client calls RpcHandlerDispatcher dispatcher = new RpcHandlerDispatcher(sessions, lifecycleManager::dispatch, executor); @@ -289,6 +599,9 @@ private Connection startCoreBody() { if (process != null) { cleanupCliProcess(process, true); } + if (inProcessTransport != null) { + closeRuntimeHost(inProcessTransport.host()); + } String stderr = serverManager.getStderrOutput(); if (!stderr.isEmpty()) { throw new CompletionException(new IOException( @@ -438,7 +751,7 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow } CompletableFuture shutdownFuture = CompletableFuture.completedFuture(null); - if (gracefulRuntimeShutdown && connection.process != null) { + if (gracefulRuntimeShutdown && (connection.process != null || connection.runtimeHost != null)) { long runtimeShutdownStartNanos = System.nanoTime(); shutdownFuture = connection.rpc.invoke("runtime.shutdown", Map.of(), Void.class) .orTimeout(RUNTIME_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS) @@ -465,6 +778,9 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow if (connection.process != null) { cleanupCliProcess(connection.process, !gracefulRuntimeShutdown || error != null); } + if (connection.runtimeHost != null) { + closeRuntimeHost(connection.runtimeHost); + } return (Void) null; }); }).thenCompose(result -> result); @@ -1353,6 +1669,9 @@ public void close() { LOG.log(Level.FINE, "Error during close", e); } finally { shutdownOwnedExecutor(); + if (closeHook != null) { + closeHook.run(); + } } } @@ -1392,7 +1711,8 @@ private void shutdownOwnedExecutor() { } } - private static record Connection(JsonRpcClient rpc, Process process, ServerRpc serverRpc) { + private static record Connection(JsonRpcClient rpc, Process process, ServerRpc serverRpc, + AutoCloseable runtimeHost) { }; } diff --git a/java/src/main/java/com/github/copilot/CopilotExperimental.java b/java/sdk/src/main/java/com/github/copilot/CopilotExperimental.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotExperimental.java rename to java/sdk/src/main/java/com/github/copilot/CopilotExperimental.java diff --git a/java/src/main/java/com/github/copilot/CopilotExperimentalProcessor.java b/java/sdk/src/main/java/com/github/copilot/CopilotExperimentalProcessor.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotExperimentalProcessor.java rename to java/sdk/src/main/java/com/github/copilot/CopilotExperimentalProcessor.java diff --git a/java/src/main/java/com/github/copilot/CopilotRequestContext.java b/java/sdk/src/main/java/com/github/copilot/CopilotRequestContext.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotRequestContext.java rename to java/sdk/src/main/java/com/github/copilot/CopilotRequestContext.java diff --git a/java/src/main/java/com/github/copilot/CopilotRequestHandler.java b/java/sdk/src/main/java/com/github/copilot/CopilotRequestHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotRequestHandler.java rename to java/sdk/src/main/java/com/github/copilot/CopilotRequestHandler.java diff --git a/java/src/main/java/com/github/copilot/CopilotRequestTransport.java b/java/sdk/src/main/java/com/github/copilot/CopilotRequestTransport.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotRequestTransport.java rename to java/sdk/src/main/java/com/github/copilot/CopilotRequestTransport.java diff --git a/java/src/main/java/com/github/copilot/CopilotSession.java b/java/sdk/src/main/java/com/github/copilot/CopilotSession.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotSession.java rename to java/sdk/src/main/java/com/github/copilot/CopilotSession.java diff --git a/java/src/main/java/com/github/copilot/CopilotWebSocketCloseStatus.java b/java/sdk/src/main/java/com/github/copilot/CopilotWebSocketCloseStatus.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotWebSocketCloseStatus.java rename to java/sdk/src/main/java/com/github/copilot/CopilotWebSocketCloseStatus.java diff --git a/java/src/main/java/com/github/copilot/CopilotWebSocketForwarder.java b/java/sdk/src/main/java/com/github/copilot/CopilotWebSocketForwarder.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotWebSocketForwarder.java rename to java/sdk/src/main/java/com/github/copilot/CopilotWebSocketForwarder.java diff --git a/java/src/main/java/com/github/copilot/CopilotWebSocketHandler.java b/java/sdk/src/main/java/com/github/copilot/CopilotWebSocketHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotWebSocketHandler.java rename to java/sdk/src/main/java/com/github/copilot/CopilotWebSocketHandler.java diff --git a/java/src/main/java/com/github/copilot/CopilotWebSocketMessage.java b/java/sdk/src/main/java/com/github/copilot/CopilotWebSocketMessage.java similarity index 100% rename from java/src/main/java/com/github/copilot/CopilotWebSocketMessage.java rename to java/sdk/src/main/java/com/github/copilot/CopilotWebSocketMessage.java diff --git a/java/src/main/java/com/github/copilot/EventErrorHandler.java b/java/sdk/src/main/java/com/github/copilot/EventErrorHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/EventErrorHandler.java rename to java/sdk/src/main/java/com/github/copilot/EventErrorHandler.java diff --git a/java/src/main/java/com/github/copilot/EventErrorPolicy.java b/java/sdk/src/main/java/com/github/copilot/EventErrorPolicy.java similarity index 100% rename from java/src/main/java/com/github/copilot/EventErrorPolicy.java rename to java/sdk/src/main/java/com/github/copilot/EventErrorPolicy.java diff --git a/java/src/main/java/com/github/copilot/ExtractedTransforms.java b/java/sdk/src/main/java/com/github/copilot/ExtractedTransforms.java similarity index 100% rename from java/src/main/java/com/github/copilot/ExtractedTransforms.java rename to java/sdk/src/main/java/com/github/copilot/ExtractedTransforms.java diff --git a/java/src/main/java/com/github/copilot/GitHubTelemetryAdapter.java b/java/sdk/src/main/java/com/github/copilot/GitHubTelemetryAdapter.java similarity index 100% rename from java/src/main/java/com/github/copilot/GitHubTelemetryAdapter.java rename to java/sdk/src/main/java/com/github/copilot/GitHubTelemetryAdapter.java diff --git a/java/src/main/java/com/github/copilot/InternalExecutorProvider.java b/java/sdk/src/main/java/com/github/copilot/InternalExecutorProvider.java similarity index 100% rename from java/src/main/java/com/github/copilot/InternalExecutorProvider.java rename to java/sdk/src/main/java/com/github/copilot/InternalExecutorProvider.java diff --git a/java/src/main/java/com/github/copilot/JsonRpcClient.java b/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java similarity index 89% rename from java/src/main/java/com/github/copilot/JsonRpcClient.java rename to java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java index 5303c4f50..550bd4ca4 100644 --- a/java/src/main/java/com/github/copilot/JsonRpcClient.java +++ b/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java @@ -18,6 +18,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -46,6 +47,7 @@ class JsonRpcClient implements AutoCloseable { private final OutputStream outputStream; private final Socket socket; private final Process process; + private final boolean ownsStreams; private final AtomicLong requestIdCounter = new AtomicLong(0); private final Map> pendingRequests = new ConcurrentHashMap<>(); private final Map> notificationHandlers = new ConcurrentHashMap<>(); @@ -53,15 +55,29 @@ class JsonRpcClient implements AutoCloseable { private volatile boolean running = true; private JsonRpcClient(InputStream inputStream, OutputStream outputStream, Socket socket, Process process) { + this(inputStream, outputStream, socket, process, false); + } + + private JsonRpcClient(InputStream inputStream, OutputStream outputStream, Socket socket, Process process, + boolean ownsStreams) { + this(inputStream, outputStream, socket, process, ownsStreams, null); + } + + private JsonRpcClient(InputStream inputStream, OutputStream outputStream, Socket socket, Process process, + boolean ownsStreams, Consumer initializer) { this.inputStream = inputStream; this.outputStream = outputStream; this.socket = socket; this.process = process; + this.ownsStreams = ownsStreams; this.readerExecutor = Executors.newSingleThreadExecutor(r -> { Thread t = new Thread(r, "jsonrpc-reader"); t.setDaemon(true); return t; }); + if (initializer != null) { + initializer.accept(this); + } startReader(); } @@ -93,6 +109,19 @@ public static JsonRpcClient fromSocket(Socket socket) throws IOException { return new JsonRpcClient(socket.getInputStream(), socket.getOutputStream(), socket, null); } + static JsonRpcClient fromSocket(Socket socket, Consumer initializer) throws IOException { + return new JsonRpcClient(socket.getInputStream(), socket.getOutputStream(), socket, null, false, initializer); + } + + /** + * Creates a JSON-RPC client over arbitrary input/output streams. The client + * takes ownership of the streams and closes them when {@link #close()} is + * called. + */ + public static JsonRpcClient fromStreams(InputStream inputStream, OutputStream outputStream) { + return new JsonRpcClient(inputStream, outputStream, null, null, true); + } + /** * Registers a handler for JSON-RPC method calls (requests/notifications from * server). @@ -344,6 +373,19 @@ public void close() { if (process != null) { process.destroy(); } + + if (ownsStreams) { + try { + inputStream.close(); + } catch (IOException e) { + LOG.log(Level.FINE, "Error closing input stream", e); + } + try { + outputStream.close(); + } catch (IOException e) { + LOG.log(Level.FINE, "Error closing output stream", e); + } + } } public boolean isConnected() { diff --git a/java/src/main/java/com/github/copilot/JsonRpcException.java b/java/sdk/src/main/java/com/github/copilot/JsonRpcException.java similarity index 100% rename from java/src/main/java/com/github/copilot/JsonRpcException.java rename to java/sdk/src/main/java/com/github/copilot/JsonRpcException.java diff --git a/java/src/main/java/com/github/copilot/LifecycleEventManager.java b/java/sdk/src/main/java/com/github/copilot/LifecycleEventManager.java similarity index 100% rename from java/src/main/java/com/github/copilot/LifecycleEventManager.java rename to java/sdk/src/main/java/com/github/copilot/LifecycleEventManager.java diff --git a/java/src/main/java/com/github/copilot/LlmInferenceAdapter.java b/java/sdk/src/main/java/com/github/copilot/LlmInferenceAdapter.java similarity index 100% rename from java/src/main/java/com/github/copilot/LlmInferenceAdapter.java rename to java/sdk/src/main/java/com/github/copilot/LlmInferenceAdapter.java diff --git a/java/src/main/java/com/github/copilot/LlmInferenceExchange.java b/java/sdk/src/main/java/com/github/copilot/LlmInferenceExchange.java similarity index 100% rename from java/src/main/java/com/github/copilot/LlmInferenceExchange.java rename to java/sdk/src/main/java/com/github/copilot/LlmInferenceExchange.java diff --git a/java/src/main/java/com/github/copilot/LlmWebSocketResponseBridge.java b/java/sdk/src/main/java/com/github/copilot/LlmWebSocketResponseBridge.java similarity index 100% rename from java/src/main/java/com/github/copilot/LlmWebSocketResponseBridge.java rename to java/sdk/src/main/java/com/github/copilot/LlmWebSocketResponseBridge.java diff --git a/java/src/main/java/com/github/copilot/LoggingHelpers.java b/java/sdk/src/main/java/com/github/copilot/LoggingHelpers.java similarity index 100% rename from java/src/main/java/com/github/copilot/LoggingHelpers.java rename to java/sdk/src/main/java/com/github/copilot/LoggingHelpers.java diff --git a/java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java b/java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java similarity index 100% rename from java/src/main/java/com/github/copilot/RpcHandlerDispatcher.java rename to java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java diff --git a/java/src/main/java/com/github/copilot/SdkProtocolVersion.java b/java/sdk/src/main/java/com/github/copilot/SdkProtocolVersion.java similarity index 100% rename from java/src/main/java/com/github/copilot/SdkProtocolVersion.java rename to java/sdk/src/main/java/com/github/copilot/SdkProtocolVersion.java diff --git a/java/src/main/java/com/github/copilot/SessionRequestBuilder.java b/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java similarity index 100% rename from java/src/main/java/com/github/copilot/SessionRequestBuilder.java rename to java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java diff --git a/java/src/main/java/com/github/copilot/SystemMessageMode.java b/java/sdk/src/main/java/com/github/copilot/SystemMessageMode.java similarity index 100% rename from java/src/main/java/com/github/copilot/SystemMessageMode.java rename to java/sdk/src/main/java/com/github/copilot/SystemMessageMode.java diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java b/java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java new file mode 100644 index 000000000..4198f08f0 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/FfiOutputStream.java @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + +final class FfiOutputStream extends OutputStream { + + private final NativeBinding nativeBinding; + private final AtomicInteger connectionId; + private final AtomicBoolean closing; + private final ReentrantLock operationLock; + + FfiOutputStream(NativeBinding nativeBinding, AtomicInteger connectionId, AtomicBoolean closing, + ReentrantLock operationLock) { + this.nativeBinding = Objects.requireNonNull(nativeBinding, "nativeBinding must not be null"); + this.connectionId = Objects.requireNonNull(connectionId, "connectionId must not be null"); + this.closing = Objects.requireNonNull(closing, "closing must not be null"); + this.operationLock = Objects.requireNonNull(operationLock, "operationLock must not be null"); + } + + @Override + public void write(int b) throws IOException { + write(new byte[]{(byte) b}, 0, 1); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + Objects.requireNonNull(b, "buffer must not be null"); + if (off < 0 || len < 0 || off + len > b.length) { + throw new IndexOutOfBoundsException("Invalid off/len for buffer of length " + b.length); + } + if (len == 0) { + return; + } + + operationLock.lock(); + try { + if (closing.get()) { + throw new IOException("The in-process runtime connection is closed."); + } + int id = connectionId.get(); + if (id == 0) { + throw new IOException("The in-process runtime connection is closed."); + } + + byte[] payload = (off == 0 && len == b.length) ? b : Arrays.copyOfRange(b, off, off + len); + if (!nativeBinding.connectionWrite(id, payload, payload.length)) { + throw new IOException("Failed to write a frame to the in-process runtime connection."); + } + } finally { + operationLock.unlock(); + } + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java b/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java new file mode 100644 index 000000000..5e7d2d461 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java @@ -0,0 +1,347 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.rpc.CopilotClientMode; +import com.github.copilot.rpc.CopilotClientOptions; + +import com.sun.jna.Pointer; + +/** + * Manages the in-process FFI runtime lifecycle. + */ +public final class FfiRuntimeHost implements AutoCloseable { + + private static final Logger LOG = Logger.getLogger(FfiRuntimeHost.class.getName()); + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private final NativeBinding nativeBinding; + private final QueueInputStream receiveStream; + private final AtomicBoolean closing = new AtomicBoolean(false); + private final AtomicBoolean disposed = new AtomicBoolean(false); + private final AtomicInteger serverId = new AtomicInteger(0); + private final AtomicInteger connectionId = new AtomicInteger(0); + private final AtomicInteger activeCallbacks = new AtomicInteger(0); + private final Object callbackDrainMonitor = new Object(); + private final ReentrantLock operationLock = new ReentrantLock(); + private final FfiOutputStream sendStream; + private final String libraryPath; + + private volatile OutboundCallback callbackRef; + + /** + * Creates an FFI runtime host using the resolved bundled native library. + * + * @throws IOException + * if the runtime library cannot be resolved + */ + public FfiRuntimeHost() throws IOException { + this(resolveLibraryPath(), null, new QueueInputStream()); + } + + FfiRuntimeHost(NativeBinding nativeBinding, String libraryPath) { + this(nativeBinding, libraryPath, new QueueInputStream()); + } + + FfiRuntimeHost(NativeBinding nativeBinding, String libraryPath, QueueInputStream receiveStream) { + this.nativeBinding = Objects.requireNonNull(nativeBinding, "nativeBinding must not be null"); + this.receiveStream = Objects.requireNonNull(receiveStream, "receiveStream must not be null"); + this.sendStream = new FfiOutputStream(this.nativeBinding, this.connectionId, this.closing, this.operationLock); + this.libraryPath = libraryPath; + } + + private FfiRuntimeHost(Path libraryPath, NativeBinding nativeBinding, QueueInputStream receiveStream) { + this(nativeBinding == null ? new JnaNativeBinding(libraryPath) : nativeBinding, libraryPath.toString(), + receiveStream); + } + + private static Path resolveLibraryPath() throws IOException { + return NativeRuntimeLoader.resolve(); + } + + /** + * Starts the in-process runtime and opens a connection. + * + * @param entrypointPath + * runtime entrypoint path passed in {@code argv_json} + * @param options + * client options used to construct {@code argv_json} and + * {@code env_json} + */ + public void start(String entrypointPath, CopilotClientOptions options) { + Objects.requireNonNull(entrypointPath, "entrypointPath must not be null"); + Objects.requireNonNull(options, "options must not be null"); + if (disposed.get()) { + throw new IllegalStateException("FfiRuntimeHost is already closed."); + } + if (serverId.get() != 0 || connectionId.get() != 0) { + throw new IllegalStateException("FfiRuntimeHost has already been started."); + } + + byte[] argvJson = buildArgvJson(entrypointPath, options); + byte[] envJson = buildEnvJson(options); + int hostHandle = runHostStartOnBlockingThread(argvJson, envJson); + if (hostHandle == 0) { + String lib = libraryPath != null ? libraryPath : ""; + throw new IllegalStateException( + "copilot_runtime_host_start failed (library '" + lib + "', entrypoint '" + entrypointPath + "')."); + } + + // Hold operationLock while publishing handles to serialize with close(). + // Recheck disposed in case close() ran while hostStart was blocking. + operationLock.lock(); + try { + if (disposed.get()) { + try { + nativeBinding.hostShutdown(hostHandle); + } catch (Throwable ignored) { + // Best effort + } + throw new IllegalStateException("FfiRuntimeHost was closed during startup."); + } + serverId.set(hostHandle); + + OutboundCallback callback = createOutboundCallback(); + callbackRef = callback; + int connHandle = nativeBinding.connectionOpen(hostHandle, callback, Pointer.NULL, null, 0, null, 0, null, + 0); + if (connHandle == 0) { + try { + nativeBinding.hostShutdown(hostHandle); + } catch (Throwable ignored) { + // Best effort + } + serverId.set(0); + callbackRef = null; + throw new IllegalStateException("copilot_runtime_connection_open failed."); + } + connectionId.set(connHandle); + LOG.fine(() -> "Started FFI runtime host. Library=" + libraryPath + ", serverId=" + hostHandle + + ", connectionId=" + connHandle); + } finally { + operationLock.unlock(); + } + } + + public InputStream getReceiveStream() { + return receiveStream; + } + + public OutputStream getSendStream() { + return sendStream; + } + + @Override + public void close() { + if (!disposed.compareAndSet(false, true)) { + return; + } + + closing.set(true); + + operationLock.lock(); + try { + int connHandle = connectionId.getAndSet(0); + if (connHandle != 0) { + try { + nativeBinding.connectionClose(connHandle); + } catch (Throwable t) { + LOG.log(Level.FINE, "Failed to close FFI connection", t); + } + } + } finally { + operationLock.unlock(); + } + + drainActiveCallbacks(); + + int hostHandle = serverId.getAndSet(0); + if (hostHandle != 0) { + try { + nativeBinding.hostShutdown(hostHandle); + } catch (Throwable t) { + LOG.log(Level.FINE, "Failed to shut down FFI host", t); + } + } + + try { + receiveStream.close(); + } catch (Throwable ignored) { + // never throw from close + } + + callbackRef = null; + } + + private void drainActiveCallbacks() { + while (activeCallbacks.get() > 0) { + synchronized (callbackDrainMonitor) { + if (activeCallbacks.get() == 0) { + return; + } + try { + callbackDrainMonitor.wait(10L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + } + } + + private OutboundCallback createOutboundCallback() { + return (userData, data, len) -> { + if (closing.get()) { + return; + } + activeCallbacks.incrementAndGet(); + try { + int length = len.intValue(); + if (closing.get() || data == null || length <= 0) { + return; + } + byte[] bytes = data.getByteArray(0, length); + if (!closing.get()) { + receiveStream.enqueue(bytes); + } + } catch (Throwable t) { + LOG.log(Level.WARNING, "Exception in FFI outbound callback", t); + } finally { + if (activeCallbacks.decrementAndGet() == 0) { + synchronized (callbackDrainMonitor) { + callbackDrainMonitor.notifyAll(); + } + } + } + }; + } + + private int runHostStartOnBlockingThread(byte[] argvJson, byte[] envJson) { + ReaderThreadFactory readerThreadFactory = new ReaderThreadFactory(); + ExecutorService executor = Executors + .newSingleThreadExecutor(runnable -> readerThreadFactory.create(runnable, "copilot-ffi-host-start")); + try { + Future future = executor.submit(() -> nativeBinding.hostStart(argvJson, argvJson.length, envJson, + envJson == null ? 0 : envJson.length)); + return future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted while starting in-process runtime host.", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException runtimeException) { + throw runtimeException; + } + throw new IllegalStateException("Failed to start in-process runtime host.", cause); + } finally { + executor.shutdownNow(); + try { + executor.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + private static byte[] buildArgvJson(String entrypointPath, CopilotClientOptions options) { + List argv = new ArrayList<>(); + if (entrypointPath.toLowerCase().endsWith(".js")) { + argv.add("node"); + } + argv.add(entrypointPath); + argv.add("--embedded-host"); + argv.add("--no-auto-update"); + + String logLevel = options.getLogLevel(); + if (logLevel != null && !logLevel.isBlank()) { + argv.add("--log-level"); + argv.add(logLevel); + } + + String gitHubToken = options.getGitHubToken(); + if (gitHubToken != null && !gitHubToken.isEmpty()) { + argv.add("--auth-token-env"); + argv.add("COPILOT_SDK_AUTH_TOKEN"); + } + + boolean useLoggedInUser = options.getUseLoggedInUser().orElse(gitHubToken == null || gitHubToken.isEmpty()); + if (!useLoggedInUser) { + argv.add("--no-auto-login"); + } + + if (options.getSessionIdleTimeoutSeconds().isPresent() + && options.getSessionIdleTimeoutSeconds().getAsInt() > 0) { + argv.add("--session-idle-timeout"); + argv.add(String.valueOf(options.getSessionIdleTimeoutSeconds().getAsInt())); + } + + if (options.isRemote()) { + argv.add("--remote"); + } + + String[] cliArgs = options.getCliArgs(); + if (cliArgs != null && cliArgs.length > 0) { + for (String arg : cliArgs) { + if (arg != null && !arg.isBlank()) { + argv.add(arg); + } + } + } + + return jsonBytes(argv); + } + + private static byte[] buildEnvJson(CopilotClientOptions options) { + Map env = new LinkedHashMap<>(); + + String token = options.getGitHubToken(); + if (token != null && !token.isEmpty()) { + env.put("COPILOT_SDK_AUTH_TOKEN", token); + } + String copilotHome = options.getCopilotHome(); + if (copilotHome != null && !copilotHome.isEmpty()) { + env.put("COPILOT_HOME", copilotHome); + } + if (options.getMode() == CopilotClientMode.EMPTY) { + env.put("COPILOT_DISABLE_KEYTAR", "1"); + } + + if (env.isEmpty()) { + return null; + } + return jsonBytes(env); + } + + private static byte[] jsonBytes(Object value) { + try { + return MAPPER.writeValueAsString(value).getBytes(StandardCharsets.UTF_8); + } catch (JsonProcessingException e) { + throw new IllegalStateException("Failed to serialize FFI JSON parameter.", e); + } + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java b/java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java new file mode 100644 index 000000000..ba3c3c40a --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/JnaNativeBinding.java @@ -0,0 +1,295 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Logger; + +/** + * JNA-backed implementation of {@link NativeBinding}. + * + *

+ * Loads the {@code runtime.node} native library by absolute path and delegates + * each {@link NativeBinding} method to the corresponding + * {@code copilot_runtime_*} C ABI export. + * + *

Library-never-unloads pattern

+ *

+ * The loaded JNA library handle is held in a {@code static} field and is never + * released. Native worker threads spawned by the runtime outlive any individual + * {@code FfiRuntimeHost} instance; unloading the library while those threads + * are active would cause a crash. This mirrors the Rust runtime's own + * {@code OnceLock>>} pattern. + * + *

Duplicate-load guard

+ *

+ * Loading a library from a different absolute path in the same JVM + * process is rejected with {@link IllegalStateException}. Loading from the + * same path more than once is silently accepted. + * + *

Active-callback tracking

+ *

+ * The {@link #activeCallbacks} counter is incremented when the native runtime + * enters the outbound callback and decremented when the callback returns. + * Callers (e.g. {@code FfiRuntimeHost}) must drain this counter to zero before + * calling {@link #connectionClose} or {@link #hostShutdown}. + * + *

Callback lifetime

+ *

+ * The native runtime can invoke an outbound callback after connection close and + * host shutdown return. Each JNA callback wrapper is therefore retained for the + * lifetime of the JVM. After host shutdown, its Java delegate is detached so a + * late native invocation safely becomes a no-op without retaining the complete + * host object graph. + * + *

GraalVM Native Image

+ *

+ * JNA callback upcalls are not supported under GraalVM Native Image. InProcess + * transport is not available in native-image executables; use subprocess + * transport instead. + */ +final class JnaNativeBinding implements NativeBinding { + + private static final Logger LOG = Logger.getLogger(JnaNativeBinding.class.getName()); + + /** + * JNA inner interface mapping the five {@code copilot_runtime_*} C ABI exports. + */ + interface CopilotRuntimeLibrary extends Library { + /** Corresponds to {@code copilot_runtime_host_start}. */ + int copilot_runtime_host_start(byte[] argvJson, SizeT argvJsonLen, byte[] envJson, SizeT envJsonLen); + + /** + * Corresponds to {@code copilot_runtime_host_shutdown}. + * + *

+ * Returns {@code byte} (not Java {@code boolean}) because the Rust ABI exports + * a one-byte {@code bool}. JNA maps Java {@code boolean} as a 32-bit C + * {@code int}, which would read three extra bytes. + */ + byte copilot_runtime_host_shutdown(int serverId); + + /** Corresponds to {@code copilot_runtime_connection_open}. */ + int copilot_runtime_connection_open(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + SizeT extSourceLen, byte[] extName, SizeT extNameLen, byte[] connToken, SizeT connTokenLen); + + /** + * Corresponds to {@code copilot_runtime_connection_write}. + * + * @see #copilot_runtime_host_shutdown for why this returns {@code byte} + */ + byte copilot_runtime_connection_write(int connectionId, byte[] data, SizeT dataLen); + + /** + * Corresponds to {@code copilot_runtime_connection_close}. + * + * @see #copilot_runtime_host_shutdown for why this returns {@code byte} + */ + byte copilot_runtime_connection_close(int connectionId); + } + + // ------------------------------------------------------------------------- + // Process-wide singleton — never unloaded + // ------------------------------------------------------------------------- + + private static final Object LOAD_LOCK = new Object(); + + /** Absolute path of the library that was first loaded into this JVM process. */ + private static volatile Path loadedPath; + + /** The loaded JNA library interface. Never released after first set. */ + private static volatile CopilotRuntimeLibrary loadedLib; + + /** + * Process-lifetime roots for JNA callback trampolines. Native code can invoke a + * callback after connection and host teardown return, so entries are never + * removed in production. + */ + private static final Set RETAINED_CALLBACKS = ConcurrentHashMap.newKeySet(); + + // ------------------------------------------------------------------------- + // Instance state + // ------------------------------------------------------------------------- + + /** + * The library interface used by this instance for all delegated calls. + * + *

+ * For the production path ({@link #JnaNativeBinding(Path)}), this is always the + * same object as {@link #loadedLib} (the static singleton). For the test path + * ({@link #JnaNativeBinding(CopilotRuntimeLibrary)}), this may be a stub or + * mock without modifying the static singleton. + */ + private final CopilotRuntimeLibrary lib; + + /** + * Count of callbacks currently executing on native threads. Must reach zero + * before {@link #connectionClose} or {@link #hostShutdown} is called. + */ + final AtomicInteger activeCallbacks = new AtomicInteger(0); + + /** + * Callback registrations keyed by connection handle. + *

+ * Registrations remain here through connection close because native callbacks + * can still arrive. Successful host shutdown detaches their Java delegates; the + * wrappers themselves remain rooted by {@link #RETAINED_CALLBACKS}. + */ + private final Map callbackRegistrations = new ConcurrentHashMap<>(); + + private static final class CallbackRegistration { + private final int serverId; + private final AtomicReference delegate; + private final AtomicInteger activeCallbacks; + private final OutboundCallback wrapper; + + private CallbackRegistration(int serverId, OutboundCallback delegate, AtomicInteger activeCallbacks) { + this.serverId = serverId; + this.delegate = new AtomicReference<>(delegate); + this.activeCallbacks = activeCallbacks; + this.wrapper = this::invoke; + } + + private void invoke(Pointer userData, Pointer data, SizeT len) { + activeCallbacks.incrementAndGet(); + try { + OutboundCallback callback = delegate.get(); + if (callback != null) { + callback.invoke(userData, data, len); + } + } finally { + activeCallbacks.decrementAndGet(); + } + } + + private void detach() { + delegate.set(null); + } + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Loads (or re-uses) the native library at the given absolute path. + * + * @param libraryPath + * absolute path to the {@code runtime.node} native library + * @throws IllegalStateException + * if a different library path has already been loaded in + * this JVM process + */ + JnaNativeBinding(Path libraryPath) { + Path absPath = libraryPath.toAbsolutePath().normalize(); + synchronized (LOAD_LOCK) { + if (loadedLib == null) { + LOG.fine(() -> "Loading native library from: " + absPath); + try { + loadedLib = Native.load(absPath.toString(), CopilotRuntimeLibrary.class); + } catch (UnsatisfiedLinkError e) { + throw new IllegalStateException("Failed to load native library from '" + absPath + "'", e); + } + loadedPath = absPath; + LOG.fine(() -> "Native library loaded: " + absPath); + } else if (!absPath.equals(loadedPath)) { + throw new IllegalStateException("An in-process FFI runtime library is already loaded from '" + + loadedPath + "'; loading a different library from '" + absPath + + "' in the same process is not supported."); + } + } + this.lib = loadedLib; + } + + /** + * Testing constructor — accepts a pre-built {@link CopilotRuntimeLibrary} + * directly, bypassing disk I/O and the static singleton guard. + * + *

+ * This constructor is package-private and intended solely for unit tests. + * + * @param library + * a {@link CopilotRuntimeLibrary} stub or mock for testing + */ + JnaNativeBinding(CopilotRuntimeLibrary library) { + // Testing seam — skip the static singleton guard. + this.lib = library; + } + + // ------------------------------------------------------------------------- + // NativeBinding delegation + // ------------------------------------------------------------------------- + + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return lib.copilot_runtime_host_start(argvJson, new SizeT(argvJsonLen), envJson, new SizeT(envJsonLen)); + } + + @Override + public boolean hostShutdown(int serverId) { + boolean shutdown = lib.copilot_runtime_host_shutdown(serverId) != 0; + if (shutdown) { + callbackRegistrations.forEach((connectionId, registration) -> { + if (registration.serverId == serverId && callbackRegistrations.remove(connectionId, registration)) { + registration.detach(); + } + }); + } + return shutdown; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + CallbackRegistration registration = new CallbackRegistration(serverId, callback, activeCallbacks); + int connectionId = lib.copilot_runtime_connection_open(serverId, registration.wrapper, userData, extSource, + new SizeT(extSourceLen), extName, new SizeT(extNameLen), connToken, new SizeT(connTokenLen)); + if (connectionId != 0) { + RETAINED_CALLBACKS.add(registration.wrapper); + callbackRegistrations.put(connectionId, registration); + } + return connectionId; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return lib.copilot_runtime_connection_write(connectionId, data, new SizeT(dataLen)) != 0; + } + + @Override + public boolean connectionClose(int connectionId) { + return lib.copilot_runtime_connection_close(connectionId) != 0; + } + + // ------------------------------------------------------------------------- + // Testing support + // ------------------------------------------------------------------------- + + /** + * Resets the process-wide static state for unit tests. + * + *

+ * Must only be called from test code. Resets + * {@link #loadedPath} and {@link #loadedLib} so that a subsequent + * {@link #JnaNativeBinding(Path)} call can load a different library. In + * production, the library is never unloaded. + */ + static void resetForTesting() { + synchronized (LOAD_LOCK) { + loadedPath = null; + loadedLib = null; + RETAINED_CALLBACKS.clear(); + } + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java b/java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java new file mode 100644 index 000000000..3aa8ca9e4 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/NativeBinding.java @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import com.sun.jna.Pointer; + +/** + * Internal abstraction over the Copilot runtime C ABI. + * + *

+ * Defines the five {@code extern "C"} entry points exposed by the native + * {@code runtime.node} library. The JNA-backed implementation + * ({@link JnaNativeBinding}) delegates to these through JNA. A future FFM + * implementation may be substituted via the multi-release JAR mechanism without + * changing callers. + * + *

+ * All classes in {@code com.github.copilot.ffi} are internal; consumers must + * not reference them directly. + * + *

C ABI entry points

+ *
    + *
  • {@code copilot_runtime_host_start} — start the runtime host
  • + *
  • {@code copilot_runtime_host_shutdown} — shut down the runtime host
  • + *
  • {@code copilot_runtime_connection_open} — open a bidirectional + * connection
  • + *
  • {@code copilot_runtime_connection_write} — write a JSON-RPC frame to the + * runtime
  • + *
  • {@code copilot_runtime_connection_close} — close a connection
  • + *
+ * + *

Wire format

+ *

+ * All frames use LSP {@code Content-Length} header framing, identical to the + * stdio transport. No special encoding or decoding is needed at the FFI + * boundary. + */ +interface NativeBinding { + + /** + * Starts the runtime host. + * + *

+ * Blocks for up to ~30 s while the worker boots and connects back. Must not be + * called on an async/reactive executor thread. + * + * @param argvJson + * UTF-8 JSON array of strings: the entrypoint and required flags + * @param argvJsonLen + * byte length of {@code argvJson} + * @param envJson + * UTF-8 JSON object of environment overrides, or {@code null} when + * empty + * @param envJsonLen + * byte length of {@code envJson}, or {@code 0} when {@code envJson} + * is null + * @return server handle ({@code 0} on failure) + */ + int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen); + + /** + * Shuts down the runtime host. + * + * @param serverId + * non-zero server handle returned by {@link #hostStart} + * @return {@code true} on success + */ + boolean hostShutdown(int serverId); + + /** + * Opens a bidirectional connection and registers the outbound data callback. + * + *

+ * The {@code extSource}, {@code extName}, and {@code connToken} parameters are + * reserved extension points. All current SDK implementations pass + * {@code null}/0 for all three. + * + * @param serverId + * non-zero server handle returned by {@link #hostStart} + * @param callback + * JNA callback invoked by the runtime on native threads when + * outbound data is available; must be held as a strong reference by + * the caller + * @param userData + * opaque cookie passed back to {@code callback} unchanged; pass + * {@link Pointer#NULL} + * @param extSource + * reserved; pass {@code null} + * @param extSourceLen + * byte length of {@code extSource}; pass {@code 0} + * @param extName + * reserved; pass {@code null} + * @param extNameLen + * byte length of {@code extName}; pass {@code 0} + * @param connToken + * reserved; pass {@code null} + * @param connTokenLen + * byte length of {@code connToken}; pass {@code 0} + * @return connection handle ({@code 0} on failure) + */ + int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, int extSourceLen, + byte[] extName, int extNameLen, byte[] connToken, int connTokenLen); + + /** + * Writes a JSON-RPC frame to the runtime. + * + *

+ * The native side copies the buffer synchronously before returning; the byte + * array does not need to survive past this call. + * + * @param connectionId + * non-zero connection handle returned by {@link #connectionOpen} + * @param data + * frame bytes + * @param dataLen + * byte length of {@code data} + * @return {@code true} on success + */ + boolean connectionWrite(int connectionId, byte[] data, int dataLen); + + /** + * Closes a connection. + * + * @param connectionId + * non-zero connection handle returned by {@link #connectionOpen} + * @return {@code true} on success + */ + boolean connectionClose(int connectionId); +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java b/java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java new file mode 100644 index 000000000..d2d06eb06 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/NativeRuntimeLoader.java @@ -0,0 +1,504 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.Properties; + +/** + * Locates the {@code runtime.node} native binary, extracts it to a versioned + * cache directory, and returns the filesystem path for JNA to load. + * + *

+ * Resolution order: + *

    + *
  1. {@code COPILOT_CLI_PATH} — checks for + * {@code runtime.node} alongside the configured CLI or in its npm + * {@code prebuilds/} directory before any classpath or platform + * work.
  2. + *
  3. Classpath resource + * {@code native//runtime.node} — extracted atomically to + * {@code ~/.copilot/runtime-cache////runtime.node}.
  4. + *
  5. PATH compatibility fallback — finds {@code copilot} on + * {@code PATH} and accepts only a flat sibling {@code runtime.node}. This + * fallback does not support normal npm or Homebrew installation layouts.
  6. + *
+ */ +public final class NativeRuntimeLoader { + + static final String RUNTIME_FILENAME = "runtime.node"; + static final String CLI_FILENAME = "copilot"; + static final String CLI_FILENAME_WINDOWS = "copilot.exe"; + static final String PLATFORM_PROPERTIES_FILENAME = "platform.properties"; + /** Environment variable that overrides where the runtime is loaded from. */ + public static final String COPILOT_CLI_PATH_ENV = "COPILOT_CLI_PATH"; + static final String VERSION_RESOURCE = "copilot-runtime.properties"; + + /** + * Abstraction for the atomic publish step, enabling deterministic failure + * injection in tests while preserving {@link StandardCopyOption#ATOMIC_MOVE} in + * production. + */ + @FunctionalInterface + interface AtomicPublisher { + /** + * Atomically publishes {@code temp} to {@code cached}. + * + * @param temp + * fully-written temporary file in the same directory as + * {@code cached} + * @param cached + * intended final location + * @throws IOException + * if the move fails + */ + void publish(Path temp, Path cached) throws IOException; + } + + /** + * Production publisher: {@link Files#move} with + * {@link StandardCopyOption#ATOMIC_MOVE}. + */ + static final AtomicPublisher DEFAULT_PUBLISHER = (temp, cached) -> { + try { + Files.move(temp, cached, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } catch (AtomicMoveNotSupportedException ex) { + throw new IllegalStateException("Filesystem does not support atomic moves; cannot safely publish " + + RUNTIME_FILENAME + " to " + cached, ex); + } catch (FileAlreadyExistsException ex) { + // Another process won the race — accept the winner if it is a valid file. + try { + if (isValidCachedFile(cached)) { + return; + } + } catch (IOException ignored) { + // fall through to the error below + } + throw new IllegalStateException( + "Concurrent extraction race: target already exists but is not a valid file: " + cached, ex); + } + }; + + private NativeRuntimeLoader() { + } + + /** + * Resolves the filesystem path to the {@code runtime.node} binary. + * + *

+ * Follows the three-step resolution order documented on this class. The + * returned path is guaranteed to refer to a regular, non-empty file at the time + * of return. + * + * @return absolute path to the {@code runtime.node} binary + * @throws IOException + * if the binary cannot be located or extracted + * @throws IllegalStateException + * if required resources are missing or extraction fails + */ + public static Path resolve() throws IOException { + String cliPathEnv = System.getenv(COPILOT_CLI_PATH_ENV); + Path cliOverride = resolveFromCliPath(cliPathEnv); + if (cliOverride != null) { + return cliOverride; + } + + ClassLoader loader = NativeRuntimeLoader.class.getClassLoader(); + String classifier = PlatformDetector.detectClassifier(); + String version = readVersion(loader); + Path cacheBase = defaultCacheBase(); + return resolve(null, findRuntimeOnPath(), cacheBase, loader, classifier, version); + } + + /** + * Resolves the copilot CLI executable from the same location as the bundled + * {@code runtime.node}. The CLI is used as {@code argv[0]} in + * {@code copilot_runtime_host_start} — the Rust runtime spawns it as a child + * process. + * + *

+ * This method calls {@link #resolve()} to locate {@code runtime.node}, then + * looks for the {@code copilot} executable in the same directory. Both + * artifacts are extracted from the classifier JAR together. + * + * @return absolute path to the {@code copilot} CLI executable + * @throws IOException + * if the CLI executable cannot be located + */ + public static Path resolveEntrypoint() throws IOException { + String configuredCli = System.getenv(COPILOT_CLI_PATH_ENV); + return resolveEntrypoint(configuredCli, resolve()); + } + + static Path resolveEntrypoint(String configuredCli, Path runtimePath) throws IOException { + if (configuredCli != null && !configuredCli.isBlank()) { + Path configuredPath = Path.of(configuredCli).toAbsolutePath().normalize(); + if (resolveFromCliPath(configuredCli) != null && Files.isRegularFile(configuredPath) + && Files.size(configuredPath) > 0) { + return configuredPath; + } + } + + Path parent = runtimePath.getParent(); + String cliName = isWindows() ? CLI_FILENAME_WINDOWS : CLI_FILENAME; + Path cliPath = parent.resolve(cliName); + if (Files.isRegularFile(cliPath) && Files.size(cliPath) > 0) { + return cliPath; + } + throw new IOException("Copilot CLI executable not found at " + cliPath + + " — the classifier JAR must contain both runtime.node and the copilot binary"); + } + + /** + * Reads the SDK version from the filtered {@code copilot-runtime.properties} + * resource. + * + * @return the version string + * @throws IOException + * if the resource cannot be read + * @throws IllegalStateException + * if the resource is missing or the version property is blank + */ + static String readVersion(ClassLoader loader) throws IOException { + URL resource = loader.getResource(VERSION_RESOURCE); + if (resource == null) { + throw new IllegalStateException("Missing version resource: " + VERSION_RESOURCE + + " — ensure Maven resource filtering has run (mvn process-resources)"); + } + Properties props = new Properties(); + try (InputStream in = resource.openStream()) { + props.load(in); + } + String version = props.getProperty("version"); + if (version == null || version.isBlank()) { + throw new IllegalStateException("Blank or missing 'version' property in " + VERSION_RESOURCE + + " — check Maven resource filtering configuration"); + } + return version; + } + + private static String readNativePackageVersion(ClassLoader loader, String classifier) throws IOException { + String resourcePath = "native/" + classifier + "/" + PLATFORM_PROPERTIES_FILENAME; + URL resource = loader.getResource(resourcePath); + if (resource == null) { + throw new FileNotFoundException("Native runtime metadata not found on classpath: " + resourcePath + + " — add the matching classifier JAR to the classpath"); + } + + Properties props = new Properties(); + try (InputStream in = resource.openStream()) { + props.load(in); + } + String version = props.getProperty("version"); + if (version == null || version.isBlank()) { + throw new IllegalStateException("Blank or missing 'version' property in " + resourcePath); + } + return version; + } + + /** + * Resolves the runtime binary path using the given parameters. Package-private + * to allow injection of test doubles in unit tests. + */ + static Path resolve(String cliPathEnv, Path cacheBase, ClassLoader loader, String classifier, String version) + throws IOException { + return resolve(cliPathEnv, cacheBase, loader, classifier, version, null, DEFAULT_PUBLISHER); + } + + static Path resolve(String cliPathEnv, String bundledCliPath, Path cacheBase, ClassLoader loader, String classifier, + String version) throws IOException { + Path bundledCliDir = bundledCliPath == null ? null : Path.of(bundledCliPath).toAbsolutePath().getParent(); + return resolve(cliPathEnv, cacheBase, loader, classifier, version, bundledCliDir, DEFAULT_PUBLISHER); + } + + static Path resolve(String cliPathEnv, Path cacheBase, ClassLoader loader, String classifier, String version, + Path bundledCliDir) throws IOException { + return resolve(cliPathEnv, cacheBase, loader, classifier, version, bundledCliDir, DEFAULT_PUBLISHER); + } + + static Path resolve(String cliPathEnv, Path cacheBase, ClassLoader loader, String classifier, String version, + Path bundledCliDir, AtomicPublisher publisher) throws IOException { + Path cliOverride = resolveFromCliPath(cliPathEnv); + if (cliOverride != null) { + return cliOverride; + } + + return resolveFromClasspathOrBundledCli(cacheBase, loader, classifier, version, bundledCliDir, publisher); + } + + /** + * Checks for {@code runtime.node} alongside the configured CLI. + * + *

+ * Checks, in order, the flat bundled layout ({@code runtime.node} directly next + * to the CLI) and the npm package layout + * ({@code prebuilds//runtime.node} next to the CLI), matching the + * two layouts the {@code @github/copilot-} packages may ship. + */ + static Path resolveFromCliPath(String cliPathStr) throws IOException { + if (cliPathStr == null || cliPathStr.isBlank()) { + return null; + } + Path cliPath = Path.of(cliPathStr).toAbsolutePath().normalize(); + Path parent = cliPath.getParent(); + + Path flat = parent.resolve(RUNTIME_FILENAME); + if (Files.isRegularFile(flat) && Files.size(flat) > 0) { + return flat; + } + + Path prebuilt = parent.resolve("prebuilds").resolve(PlatformDetector.detectClassifier()) + .resolve(RUNTIME_FILENAME); + if (Files.isRegularFile(prebuilt) && Files.size(prebuilt) > 0) { + return prebuilt; + } + + return null; + } + + /** + * Extracts the classpath resource {@code native//runtime.node} to + * the versioned cache directory, using an atomic publish sequence to prevent + * readers from observing a partially-written file. Uses + * {@link #DEFAULT_PUBLISHER}. + * + * @param cacheBase + * root cache directory (e.g. {@code ~/.copilot/runtime-cache}) + * @param loader + * class loader used to open the classpath resource + * @param classifier + * platform classifier (e.g. {@code linux-x64}) + * @param version + * SDK version used as part of the cache key + * @return path to the extracted {@code runtime.node} binary + * @throws IOException + * if I/O or the atomic rename fails + * @throws IllegalStateException + * if the classpath resource is missing or empty, or if the + * filesystem does not support atomic moves + */ + static Path extractToCache(Path cacheBase, ClassLoader loader, String classifier, String version) + throws IOException { + return extractToCache(cacheBase, loader, classifier, version, DEFAULT_PUBLISHER); + } + + /** + * Extracts the classpath resource to the versioned cache directory with an + * injectable publisher. Package-private for unit tests. + * + * @param cacheBase + * root cache directory + * @param loader + * class loader used to open the classpath resource + * @param classifier + * platform classifier + * @param version + * SDK version used as part of the cache key + * @param publisher + * atomic publish implementation + * @return path to the extracted {@code runtime.node} binary + * @throws IOException + * if I/O or the atomic rename fails + * @throws IllegalStateException + * if the classpath resource is missing or empty + */ + static Path extractToCache(Path cacheBase, ClassLoader loader, String classifier, String version, + AtomicPublisher publisher) throws IOException { + String resourcePath = "native/" + classifier + "/" + RUNTIME_FILENAME; + String nativeVersion = readNativePackageVersion(loader, classifier); + Path cacheDir = cacheBase.resolve(version).resolve(nativeVersion).resolve(classifier); + Path cached = cacheDir.resolve(RUNTIME_FILENAME); + + // Step 1 — fast path: return an existing valid cache entry. + if (isValidCachedFile(cached)) { + extractCliToCache(cacheDir, loader, classifier, publisher); + return cached; + } + + // Step 2 — locate the classpath resource before creating any files. + URL resource = loader.getResource(resourcePath); + if (resource == null) { + throw new FileNotFoundException("Native runtime not found on classpath: " + resourcePath + + " — add the matching classifier JAR to the classpath"); + } + + // Step 3 — ensure the cache directory exists. + Files.createDirectories(cacheDir); + + // Step 4 — write to a unique sibling temp file, then publish atomically. + Path temp = Files.createTempFile(cacheDir, "runtime-tmp-", ".node"); + try { + copyResourceToTemp(resource, resourcePath, temp); + publisher.publish(temp, cached); + } finally { + tryDelete(temp); + } + + // Step 5 — also extract the copilot CLI executable alongside runtime.node. + extractCliToCache(cacheDir, loader, classifier, publisher); + + return cached; + } + + /** + * Extracts the copilot CLI executable from the classpath to the same cache + * directory as {@code runtime.node}. Idempotent — skips extraction if already + * present and valid. + */ + static void extractCliToCache(Path cacheDir, ClassLoader loader, String classifier, AtomicPublisher publisher) + throws IOException { + String cliName = isWindows() ? CLI_FILENAME_WINDOWS : CLI_FILENAME; + String cliResourcePath = "native/" + classifier + "/" + cliName; + Path cachedCli = cacheDir.resolve(cliName); + + if (isValidCachedCli(cachedCli)) { + return; + } + + URL cliResource = loader.getResource(cliResourcePath); + if (cliResource == null) { + // CLI not on classpath — this is allowed for the COPILOT_CLI_PATH fallback + // path but will fail later in resolveEntrypoint() if InProcess is selected. + return; + } + + Files.createDirectories(cacheDir); + Path temp = Files.createTempFile(cacheDir, "cli-tmp-", ""); + try { + copyResourceToTemp(cliResource, cliResourcePath, temp); + makeExecutable(temp); + publisher.publish(temp, cachedCli); + } finally { + tryDelete(temp); + } + + if (!isValidCachedCli(cachedCli)) { + throw new IOException("Published Copilot CLI is not a non-empty executable file: " + cachedCli); + } + } + + /** + * Tries source 2 (classpath extraction) first and falls back to source 3 + * (bundled-CLI sibling) only when the classpath resource is absent. + */ + private static Path resolveFromClasspathOrBundledCli(Path cacheBase, ClassLoader loader, String classifier, + String version, Path bundledCliDir, AtomicPublisher publisher) throws IOException { + // Source 2: classpath resource. + try { + return extractToCache(cacheBase, loader, classifier, version, publisher); + } catch (FileNotFoundException ex) { + // Source 3: runtime.node alongside the bundled CLI binary. + if (bundledCliDir != null) { + Path candidate = bundledCliDir.resolve(RUNTIME_FILENAME); + try { + if (isValidCachedFile(candidate)) { + return candidate; + } + } catch (IOException ignored) { + // fall through and rethrow the original classpath error + } + } + throw ex; + } + } + + private static boolean isValidCachedFile(Path path) throws IOException { + if (!Files.isRegularFile(path)) { + return false; + } + return Files.size(path) > 0; + } + + private static boolean isValidCachedCli(Path path) throws IOException { + return isValidCachedFile(path) && (isWindows() || Files.isExecutable(path)); + } + + private static void makeExecutable(Path path) throws IOException { + if (isWindows()) { + return; + } + final boolean executableSet; + try { + executableSet = path.toFile().setExecutable(true, false); + } catch (SecurityException ex) { + throw new IOException("Failed to make Copilot CLI executable: " + path, ex); + } + if (!executableSet || !Files.isExecutable(path)) { + throw new IOException("Failed to make Copilot CLI executable: " + path); + } + } + + private static void copyResourceToTemp(URL resource, String resourcePath, Path temp) throws IOException { + try (InputStream in = resource.openStream()) { + long bytesWritten = Files.copy(in, temp, StandardCopyOption.REPLACE_EXISTING); + if (bytesWritten == 0) { + throw new IllegalStateException("Classpath resource is empty: " + resourcePath); + } + } + // Flush OS buffers to durable storage before the atomic rename. + try (FileChannel channel = FileChannel.open(temp, StandardOpenOption.WRITE)) { + channel.force(true); + } + } + + /** + * Finds the Copilot CLI executable on the {@code PATH}. + * + * @return the absolute CLI path, or {@code null} if none was found + */ + public static String findRuntimeOnPath() { + String pathValue = System.getenv("PATH"); + if (pathValue == null || pathValue.isBlank()) { + return null; + } + + String[] executableNames = isWindows() + ? new String[]{"copilot.exe", "copilot.cmd", "copilot.bat", "copilot"} + : new String[]{"copilot"}; + for (String directory : pathValue.split(java.io.File.pathSeparator)) { + if (directory.isBlank()) { + continue; + } + for (String executableName : executableNames) { + Path candidate = Path.of(directory, executableName); + if (Files.isRegularFile(candidate)) { + try { + return candidate.toRealPath().toString(); + } catch (IOException ignored) { + return candidate.toAbsolutePath().normalize().toString(); + } + } + } + } + return null; + } + + private static boolean isWindows() { + return System.getProperty("os.name", "").toLowerCase(java.util.Locale.ROOT).contains("win"); + } + + private static void tryDelete(Path path) { + try { + Files.deleteIfExists(path); + } catch (IOException ignored) { + // Best-effort cleanup; an orphaned temp file in the cache directory is benign. + } + } + + private static Path defaultCacheBase() { + return Path.of(System.getProperty("user.home"), ".copilot", "runtime-cache"); + } + +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java b/java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java new file mode 100644 index 000000000..597feea9e --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/OutboundCallback.java @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import com.sun.jna.Callback; +import com.sun.jna.Pointer; + +/** + * JNA callback interface for the runtime-to-Java outbound data path. + * + *

+ * The native runtime invokes this callback on a native thread when data is + * ready to be delivered to the Java side. JNA automatically attaches the native + * thread to the JVM before dispatching the callback. + * + *

+ * Buffer lifetime: The {@code data} pointer is only valid for + * the duration of the callback invocation. Implementations must copy the bytes + * out (e.g. {@code data.getByteArray(0, len)}) before returning. + * + *

+ * GC protection: Instances must be held as strong-reference + * fields for as long as native code may invoke the callback. If the instance is + * garbage-collected, the function pointer becomes dangling and the JVM will + * crash. + */ +@FunctionalInterface +interface OutboundCallback extends Callback { + + /** + * Invoked by the native runtime when outbound data is available. + * + * @param userData + * opaque cookie passed through unchanged from + * {@code copilot_runtime_connection_open}; always + * {@code Pointer.NULL} in this SDK + * @param data + * pointer to the outbound byte buffer; valid only for the duration + * of this invocation + * @param len + * byte length of the buffer pointed to by {@code data} + */ + void invoke(Pointer userData, Pointer data, SizeT len); +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/PlatformDetector.java b/java/sdk/src/main/java/com/github/copilot/ffi/PlatformDetector.java new file mode 100644 index 000000000..466cf794b --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/PlatformDetector.java @@ -0,0 +1,303 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Detects the current platform and resolves the runtime classifier. + */ +public final class PlatformDetector { + private static final int ELF_HEADER_PROBE_BYTES = 2048; + private static final int ELF_MAGIC_0 = 0x7F; + private static final int ELF_MAGIC_1 = 'E'; + private static final int ELF_MAGIC_2 = 'L'; + private static final int ELF_MAGIC_3 = 'F'; + private static final int ELF_CLASS_32 = 1; + private static final int ELF_CLASS_64 = 2; + private static final int ELF_DATA_LITTLE_ENDIAN = 1; + private static final int ELF_DATA_BIG_ENDIAN = 2; + private static final int ELF32_PROGRAM_HEADER_SIZE = 32; + private static final int ELF64_PROGRAM_HEADER_SIZE = 56; + private static final int PT_INTERP = 3; + + private static final Set SUPPORTED_CLASSIFIERS = Set.of("linux-x64", "linux-arm64", "linuxmusl-x64", + "linuxmusl-arm64", "darwin-x64", "darwin-arm64", "win32-x64", "win32-arm64"); + + private static final Map CLASSIFIER_BY_KEY = Map.ofEntries( + Map.entry(new ClassifierKey("linux", "x64", LinuxLibc.GLIBC), "linux-x64"), + Map.entry(new ClassifierKey("linux", "arm64", LinuxLibc.GLIBC), "linux-arm64"), + Map.entry(new ClassifierKey("linux", "x64", LinuxLibc.MUSL), "linuxmusl-x64"), + Map.entry(new ClassifierKey("linux", "arm64", LinuxLibc.MUSL), "linuxmusl-arm64"), + Map.entry(new ClassifierKey("linux", "x64", LinuxLibc.UNKNOWN), "linux-x64"), + Map.entry(new ClassifierKey("linux", "arm64", LinuxLibc.UNKNOWN), "linux-arm64"), + Map.entry(new ClassifierKey("darwin", "x64", LinuxLibc.UNKNOWN), "darwin-x64"), + Map.entry(new ClassifierKey("darwin", "arm64", LinuxLibc.UNKNOWN), "darwin-arm64"), + Map.entry(new ClassifierKey("win32", "x64", LinuxLibc.UNKNOWN), "win32-x64"), + Map.entry(new ClassifierKey("win32", "arm64", LinuxLibc.UNKNOWN), "win32-arm64")); + + private PlatformDetector() { + } + + /** + * Linux C runtime classification. + */ + public enum LinuxLibc { + /** GNU libc runtime. */ + GLIBC, + + /** musl libc runtime. */ + MUSL, + + /** Unknown or undetectable runtime. */ + UNKNOWN + } + + /** + * Detects the normalized operating system identifier. + * + * @return {@code darwin}, {@code linux}, or {@code win32} + */ + public static String detectOs() { + return detectOs(System.getProperty("os.name", "")); + } + + /** + * Detects the normalized architecture identifier. + * + * @return {@code x64} or {@code arm64} + */ + public static String detectArch() { + return detectArch(System.getProperty("os.arch", "")); + } + + /** + * Detects the Linux libc variant using {@code /proc/self/exe} PT_INTERP. + * + * @return Linux libc classification; {@code UNKNOWN} on non-Linux or parse + * failures + */ + public static LinuxLibc detectLinuxLibc() { + if (!"linux".equals(detectOs())) { + return LinuxLibc.UNKNOWN; + } + return detectLinuxLibc(Path.of("/proc/self/exe")); + } + + /** + * Detects the runtime classifier for the current platform. + * + * @return platform classifier string + */ + public static String detectClassifier() { + return detectClassifier(detectOs(), detectArch(), detectLinuxLibc()); + } + + static String detectOs(String osName) { + String normalized = osName.toLowerCase(Locale.ROOT); + if (normalized.contains("mac") || normalized.contains("darwin")) { + return "darwin"; + } + if (normalized.contains("win")) { + return "win32"; + } + if (normalized.contains("linux")) { + return "linux"; + } + throw new IllegalStateException("Unsupported os.name: " + osName); + } + + static String detectArch(String osArch) { + String normalized = osArch.toLowerCase(Locale.ROOT).replace('-', '_'); + if (normalized.equals("amd64") || normalized.equals("x86_64") || normalized.equals("x64")) { + return "x64"; + } + if (normalized.equals("aarch64") || normalized.equals("arm64")) { + return "arm64"; + } + throw new IllegalStateException("Unsupported os.arch: " + osArch); + } + + static LinuxLibc detectLinuxLibc(Path executablePath) { + try { + return detectLinuxLibc(readPrefix(executablePath, ELF_HEADER_PROBE_BYTES)); + } catch (IOException ex) { + return LinuxLibc.UNKNOWN; + } + } + + static LinuxLibc detectLinuxLibc(byte[] elfPrefix) throws IOException { + String interpreter = readElfPtInterp(elfPrefix); + if (interpreter.contains("/ld-musl-")) { + return LinuxLibc.MUSL; + } + if (interpreter.contains("/ld-linux-")) { + return LinuxLibc.GLIBC; + } + return LinuxLibc.UNKNOWN; + } + + static String detectClassifier(String os, String arch, LinuxLibc linuxLibc) { + LinuxLibc classifierLibc = "linux".equals(os) ? linuxLibc : LinuxLibc.UNKNOWN; + String classifier = CLASSIFIER_BY_KEY.get(new ClassifierKey(os, arch, classifierLibc)); + if (classifier == null || !SUPPORTED_CLASSIFIERS.contains(classifier)) { + throw new IllegalStateException( + "Unsupported platform tuple: os=" + os + ", arch=" + arch + ", libc=" + classifierLibc); + } + return classifier; + } + + static Set supportedClassifiers() { + return SUPPORTED_CLASSIFIERS; + } + + private static String readElfPtInterp(byte[] probe) throws IOException { + int size = probe.length; + if (size < 64) { + throw new IOException("ELF probe too small: " + size + " bytes"); + } + if ((probe[0] & 0xFF) != ELF_MAGIC_0 || (probe[1] & 0xFF) != ELF_MAGIC_1 || (probe[2] & 0xFF) != ELF_MAGIC_2 + || (probe[3] & 0xFF) != ELF_MAGIC_3) { + throw new IOException("Not an ELF executable"); + } + + int elfClass = probe[4] & 0xFF; + int elfData = probe[5] & 0xFF; + if (elfData != ELF_DATA_LITTLE_ENDIAN && elfData != ELF_DATA_BIG_ENDIAN) { + throw new IOException("Unsupported ELF data encoding: " + elfData); + } + boolean littleEndian = elfData == ELF_DATA_LITTLE_ENDIAN; + + long phoff; + int phentsize; + int phnum; + int minimumPhentsize; + if (elfClass == ELF_CLASS_64) { + phoff = readUInt64(probe, 32, littleEndian); + phentsize = readUInt16(probe, 54, littleEndian); + phnum = readUInt16(probe, 56, littleEndian); + minimumPhentsize = ELF64_PROGRAM_HEADER_SIZE; + } else if (elfClass == ELF_CLASS_32) { + phoff = readUInt32(probe, 28, littleEndian); + phentsize = readUInt16(probe, 42, littleEndian); + phnum = readUInt16(probe, 44, littleEndian); + minimumPhentsize = ELF32_PROGRAM_HEADER_SIZE; + } else { + throw new IOException("Unsupported ELF class: " + elfClass); + } + + if (phoff < 0 || phoff >= size) { + throw new IOException("Program header table offset outside probe window: " + phoff); + } + if (phentsize < minimumPhentsize || phnum <= 0) { + throw new IOException("Invalid ELF program header metadata: phentsize=" + phentsize + ", phnum=" + phnum); + } + + for (int i = 0; i < phnum; i++) { + long baseLong = phoff + ((long) i * phentsize); + if (baseLong < 0 || baseLong > Integer.MAX_VALUE) { + break; + } + int base = (int) baseLong; + if (base + phentsize > size) { + break; + } + + long pType = readUInt32(probe, base, littleEndian); + if (pType != PT_INTERP) { + continue; + } + + long pOffset; + long pFileSize; + if (elfClass == ELF_CLASS_64) { + pOffset = readUInt64(probe, base + 8, littleEndian); + pFileSize = readUInt64(probe, base + 32, littleEndian); + } else { + pOffset = readUInt32(probe, base + 4, littleEndian); + pFileSize = readUInt32(probe, base + 16, littleEndian); + } + + if (pOffset < 0 || pFileSize <= 0 || pOffset > Integer.MAX_VALUE || pFileSize > Integer.MAX_VALUE) { + throw new IOException("Invalid PT_INTERP bounds"); + } + + int start = (int) pOffset; + int end = start + (int) pFileSize; + if (end > size) { + throw new IOException("PT_INTERP extends past probe window; increase probe size"); + } + + int nulIndex = start; + while (nulIndex < end && probe[nulIndex] != 0) { + nulIndex++; + } + if (nulIndex == start) { + throw new IOException("Empty PT_INTERP segment"); + } + return new String(probe, start, nulIndex - start, StandardCharsets.UTF_8); + } + + throw new IOException("ELF PT_INTERP segment not found"); + } + + private static byte[] readPrefix(Path path, int maxBytes) throws IOException { + byte[] buffer = new byte[maxBytes]; + int total = 0; + try (InputStream in = Files.newInputStream(path)) { + while (total < maxBytes) { + int read = in.read(buffer, total, maxBytes - total); + if (read < 0) { + break; + } + total += read; + } + } + byte[] resized = new byte[total]; + System.arraycopy(buffer, 0, resized, 0, total); + return resized; + } + + private static int readUInt16(byte[] data, int offset, boolean littleEndian) { + int b0 = data[offset] & 0xFF; + int b1 = data[offset + 1] & 0xFF; + return littleEndian ? (b0 | (b1 << 8)) : ((b0 << 8) | b1); + } + + private static long readUInt32(byte[] data, int offset, boolean littleEndian) { + long b0 = data[offset] & 0xFFL; + long b1 = data[offset + 1] & 0xFFL; + long b2 = data[offset + 2] & 0xFFL; + long b3 = data[offset + 3] & 0xFFL; + if (littleEndian) { + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + } + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + private static long readUInt64(byte[] data, int offset, boolean littleEndian) { + long result = 0L; + if (littleEndian) { + for (int i = 7; i >= 0; i--) { + result = (result << 8) | (data[offset + i] & 0xFFL); + } + return result; + } + for (int i = 0; i < 8; i++) { + result = (result << 8) | (data[offset + i] & 0xFFL); + } + return result; + } + + private record ClassifierKey(String os, String arch, LinuxLibc libc) { + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/QueueInputStream.java b/java/sdk/src/main/java/com/github/copilot/ffi/QueueInputStream.java new file mode 100644 index 000000000..977182d5f --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/QueueInputStream.java @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * {@link InputStream} backed by a {@link BlockingQueue} of byte-array chunks. + * + *

+ * Used by the in-process FFI transport to bridge native callback frames into + * the JSON-RPC reader. + */ +public class QueueInputStream extends InputStream { + + private static final byte[] EOF_SENTINEL = new byte[0]; + + private final BlockingQueue queue; + private final AtomicBoolean closed = new AtomicBoolean(false); + + private byte[] currentChunk; + private int currentOffset; + private boolean eof; + + /** + * Creates a queue-backed input stream with an unbounded queue. + */ + public QueueInputStream() { + this(new LinkedBlockingQueue<>()); + } + + /** + * Testing constructor that injects a queue implementation. + * + * @param queue + * backing queue + */ + QueueInputStream(BlockingQueue queue) { + this.queue = Objects.requireNonNull(queue, "queue must not be null"); + } + + void enqueue(byte[] bytes) { + if (bytes == null || bytes.length == 0 || closed.get()) { + return; + } + queue.offer(bytes); + } + + @Override + public int read() throws IOException { + byte[] one = new byte[1]; + int read = read(one, 0, 1); + if (read == -1) { + return -1; + } + return one[0] & 0xFF; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + Objects.requireNonNull(b, "buffer must not be null"); + if (off < 0 || len < 0 || off + len > b.length) { + throw new IndexOutOfBoundsException("Invalid off/len for buffer of length " + b.length); + } + if (len == 0) { + return 0; + } + if (eof) { + return -1; + } + + while (currentChunk == null || currentOffset >= currentChunk.length) { + byte[] next; + try { + next = queue.take(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while waiting for callback data", e); + } + if (next == EOF_SENTINEL) { + eof = true; + return -1; + } + if (next.length == 0) { + continue; + } + currentChunk = next; + currentOffset = 0; + } + + int available = currentChunk.length - currentOffset; + int toCopy = Math.min(available, len); + System.arraycopy(currentChunk, currentOffset, b, off, toCopy); + currentOffset += toCopy; + return toCopy; + } + + @Override + public int available() { + if (currentChunk == null || currentOffset >= currentChunk.length) { + return 0; + } + return currentChunk.length - currentOffset; + } + + @Override + public void close() { + if (closed.compareAndSet(false, true)) { + queue.offer(EOF_SENTINEL); + } + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/ReaderThreadFactory.java b/java/sdk/src/main/java/com/github/copilot/ffi/ReaderThreadFactory.java new file mode 100644 index 000000000..b0824fa9a --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/ReaderThreadFactory.java @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +/** + * Creates reader threads for FFI queue consumption. + * + *

+ * Baseline (JDK 17) implementation creates a daemon platform thread. The JDK 25 + * multi-release overlay switches this to a virtual thread with the same + * package-private API. + */ +final class ReaderThreadFactory { + + Thread create(Runnable task, String name) { + Thread thread = new Thread(task, name); + thread.setDaemon(true); + return thread; + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/ffi/SizeT.java b/java/sdk/src/main/java/com/github/copilot/ffi/SizeT.java new file mode 100644 index 000000000..5bec1c327 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/ffi/SizeT.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import com.sun.jna.IntegerType; +import com.sun.jna.Native; + +/** + * JNA type mapping for the C {@code size_t} type. + * + *

+ * {@code size_t} is pointer-sized: 8 bytes on 64-bit platforms, 4 bytes on + * 32-bit. Using Java {@code int} (always 4 bytes) would silently truncate on + * 64-bit, and using {@link com.sun.jna.NativeLong} would be wrong on Windows + * x64 where C {@code long} is 4 bytes but {@code size_t} is 8 bytes. + * + *

+ * This class uses {@link Native#SIZE_T_SIZE} so JNA marshals the correct width + * on every platform. + */ +public final class SizeT extends IntegerType { + + /** Zero-valued instance; required by JNA for return-type instantiation. */ + public SizeT() { + this(0); + } + + /** + * Creates a {@code size_t} with the given value. + * + * @param value + * the numeric value (unsigned, but stored as signed long) + */ + public SizeT(long value) { + super(Native.SIZE_T_SIZE, value, true); + } +} diff --git a/java/src/main/java/com/github/copilot/package-info.java b/java/sdk/src/main/java/com/github/copilot/package-info.java similarity index 100% rename from java/src/main/java/com/github/copilot/package-info.java rename to java/sdk/src/main/java/com/github/copilot/package-info.java diff --git a/java/src/main/java/com/github/copilot/rpc/AgentInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AgentInfo.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AgentInfo.java diff --git a/java/src/main/java/com/github/copilot/rpc/AgentMode.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentMode.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AgentMode.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AgentMode.java diff --git a/java/src/main/java/com/github/copilot/rpc/AgentStopHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AgentStopHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/AgentStopHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AgentStopHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AgentStopHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/Attachment.java b/java/sdk/src/main/java/com/github/copilot/rpc/Attachment.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/Attachment.java rename to java/sdk/src/main/java/com/github/copilot/rpc/Attachment.java diff --git a/java/src/main/java/com/github/copilot/rpc/AutoModeSwitchHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AutoModeSwitchHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/AutoModeSwitchInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AutoModeSwitchInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/AutoModeSwitchRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AutoModeSwitchRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/AutoModeSwitchResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AutoModeSwitchResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AutoModeSwitchResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/AzureOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/AzureOptions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/AzureOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/AzureOptions.java diff --git a/java/src/main/java/com/github/copilot/rpc/BearerTokenProvider.java b/java/sdk/src/main/java/com/github/copilot/rpc/BearerTokenProvider.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/BearerTokenProvider.java rename to java/sdk/src/main/java/com/github/copilot/rpc/BearerTokenProvider.java diff --git a/java/src/main/java/com/github/copilot/rpc/BlobAttachment.java b/java/sdk/src/main/java/com/github/copilot/rpc/BlobAttachment.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/BlobAttachment.java rename to java/sdk/src/main/java/com/github/copilot/rpc/BlobAttachment.java diff --git a/java/src/main/java/com/github/copilot/rpc/BuiltInTools.java b/java/sdk/src/main/java/com/github/copilot/rpc/BuiltInTools.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/BuiltInTools.java rename to java/sdk/src/main/java/com/github/copilot/rpc/BuiltInTools.java diff --git a/java/src/main/java/com/github/copilot/rpc/CapiSessionOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CapiSessionOptions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CapiSessionOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CapiSessionOptions.java diff --git a/java/src/main/java/com/github/copilot/rpc/CloudSessionOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CloudSessionOptions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CloudSessionOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CloudSessionOptions.java diff --git a/java/src/main/java/com/github/copilot/rpc/CloudSessionRepository.java b/java/sdk/src/main/java/com/github/copilot/rpc/CloudSessionRepository.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CloudSessionRepository.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CloudSessionRepository.java diff --git a/java/src/main/java/com/github/copilot/rpc/CommandContext.java b/java/sdk/src/main/java/com/github/copilot/rpc/CommandContext.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CommandContext.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CommandContext.java diff --git a/java/src/main/java/com/github/copilot/rpc/CommandDefinition.java b/java/sdk/src/main/java/com/github/copilot/rpc/CommandDefinition.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CommandDefinition.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CommandDefinition.java diff --git a/java/src/main/java/com/github/copilot/rpc/CommandHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/CommandHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CommandHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CommandHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/CommandWireDefinition.java b/java/sdk/src/main/java/com/github/copilot/rpc/CommandWireDefinition.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CommandWireDefinition.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CommandWireDefinition.java diff --git a/java/src/main/java/com/github/copilot/rpc/CopilotClientMode.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientMode.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CopilotClientMode.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientMode.java diff --git a/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java similarity index 94% rename from java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java index 0d4494d73..ace972e5b 100644 --- a/java/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java @@ -51,6 +51,7 @@ public class CopilotClientOptions { private String[] cliArgs; private String cliPath; private String cliUrl; + private RuntimeConnection connection; private String copilotHome; private String cwd; private Map environment; @@ -204,6 +205,41 @@ public CopilotClientOptions setCliUrl(String cliUrl) { return this; } + /** + * Gets the connection that selects how the client reaches the Copilot runtime. + * + * @return the connection, or {@code null} to infer the transport from + * {@link #isUseStdio()}, {@link #getCliUrl()} and {@link #getCliPath()} + */ + @JsonIgnore + @CopilotExperimental + public RuntimeConnection getConnection() { + return connection; + } + + /** + * Sets the connection that selects how the client reaches the Copilot runtime. + *

+ * When set, the connection takes precedence over the transport-selecting + * options {@link #setUseStdio(boolean)}, {@link #setCliUrl(String)}, + * {@link #setCliPath(String)}, {@link #setPort(int)} and + * {@link #setTcpConnectionToken(String)}; combining a connection with + * conflicting values for any of those options makes the client constructor + * throw {@link IllegalArgumentException}. Values that match what the connection + * implies are accepted, so the same options instance can be reused across + * multiple client constructions. + * + * @param connection + * the connection, or {@code null} to infer the transport from the + * individual transport options + * @return this options instance for method chaining + */ + @CopilotExperimental + public CopilotClientOptions setConnection(RuntimeConnection connection) { + this.connection = connection; + return this; + } + /** * Gets the base directory for Copilot data (session state, config, etc.). * @@ -248,13 +284,11 @@ public String getCwd() { * Sets the working directory for the CLI process. * * @param cwd - * the working directory path (must not be {@code null} or empty) + * the working directory path, or {@code null} to clear * @return this options instance for method chaining - * @throws IllegalArgumentException - * if {@code cwd} is {@code null} or empty */ public CopilotClientOptions setCwd(String cwd) { - this.cwd = Objects.requireNonNull(cwd, "cwd must not be null"); + this.cwd = cwd; return this; } @@ -754,6 +788,7 @@ public CopilotClientOptions clone() { copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null; copy.cliPath = this.cliPath; copy.cliUrl = this.cliUrl; + copy.connection = this.connection; copy.copilotHome = this.copilotHome; copy.cwd = this.cwd; copy.environment = this.environment != null ? new java.util.HashMap<>(this.environment) : null; diff --git a/java/src/main/java/com/github/copilot/rpc/CopilotExpAssignmentResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotExpAssignmentResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CopilotExpAssignmentResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CopilotExpAssignmentResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CreateSessionResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/CustomAgentConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/DefaultAgentConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/DefaultAgentConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/DefaultAgentConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/DefaultAgentConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/DeleteSessionResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/DeleteSessionResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/DeleteSessionResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/DeleteSessionResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationContext.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationContext.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationContext.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationContext.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationParams.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationParams.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationParams.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationParams.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationResult.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationResult.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationResult.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationResultAction.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationResultAction.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationResultAction.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationResultAction.java diff --git a/java/src/main/java/com/github/copilot/rpc/ElicitationSchema.java b/java/sdk/src/main/java/com/github/copilot/rpc/ElicitationSchema.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ElicitationSchema.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ElicitationSchema.java diff --git a/java/src/main/java/com/github/copilot/rpc/ExitPlanModeHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ExitPlanModeHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/ExitPlanModeInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ExitPlanModeInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/ExitPlanModeRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ExitPlanModeRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/ExitPlanModeResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeResult.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ExitPlanModeResult.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ExitPlanModeResult.java diff --git a/java/src/main/java/com/github/copilot/rpc/ExpConfigEntry.java b/java/sdk/src/main/java/com/github/copilot/rpc/ExpConfigEntry.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ExpConfigEntry.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ExpConfigEntry.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetAuthStatusResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetAuthStatusResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetAuthStatusResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetAuthStatusResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetForegroundSessionResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetForegroundSessionResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetForegroundSessionResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetForegroundSessionResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetLastSessionIdResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetLastSessionIdResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetLastSessionIdResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetLastSessionIdResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetMessagesResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetMessagesResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetMessagesResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetMessagesResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetModelsResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetModelsResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetModelsResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetModelsResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetSessionMetadataResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetSessionMetadataResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetSessionMetadataResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetSessionMetadataResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GetStatusResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/GetStatusResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GetStatusResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GetStatusResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/GitHubMcpToolConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/HookInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/HookInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/HookInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/HookInvocation.java diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/InProcessRuntimeConnection.java b/java/sdk/src/main/java/com/github/copilot/rpc/InProcessRuntimeConnection.java new file mode 100644 index 000000000..274f8b89d --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/InProcessRuntimeConnection.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.github.copilot.CopilotExperimental; + +/** + * Hosts the runtime in-process by loading its native library and communicating + * over the C ABI — no child process is spawned by the SDK for JSON-RPC + * transport. Construct with {@link RuntimeConnection#forInProcess()}. + *

+ * The in-process runtime is self-contained: it carries everything it needs and + * requires no external installation. Because it runs inside the host process, + * per-client process settings ({@code environment}, {@code telemetry}, + * {@code cwd}, and {@code cliArgs}) are rejected; configure those on the host + * process instead, or use a child-process connection. + * + * @since 1.0.0 + */ +@CopilotExperimental +public final class InProcessRuntimeConnection extends RuntimeConnection { + + InProcessRuntimeConnection() { + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/InfiniteSessionConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/InfiniteSessionConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/InfiniteSessionConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/InfiniteSessionConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/InputOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/InputOptions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/InputOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/InputOptions.java diff --git a/java/src/main/java/com/github/copilot/rpc/JsonRpcError.java b/java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcError.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/JsonRpcError.java rename to java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcError.java diff --git a/java/src/main/java/com/github/copilot/rpc/JsonRpcRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/JsonRpcRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/JsonRpcResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/JsonRpcResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/JsonRpcResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/LargeToolOutputConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/LargeToolOutputConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/LargeToolOutputConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/LargeToolOutputConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ListSessionsResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/ListSessionsResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ListSessionsResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ListSessionsResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettings.java b/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettings.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ManagedSettings.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettings.java diff --git a/java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java b/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpAuthHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpAuthHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpAuthHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpAuthHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpAuthInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpAuthInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpAuthInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpAuthInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpAuthRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpAuthRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpAuthRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpAuthRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpAuthResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpAuthResult.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpAuthResult.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpAuthResult.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpAuthToken.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpAuthToken.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpAuthToken.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpAuthToken.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpHttpServerConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpHttpServerConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpHttpServerConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpHttpServerConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpServerConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpServerConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpServerConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpServerConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/McpStdioServerConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/McpStdioServerConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/McpStdioServerConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/McpStdioServerConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/MemoryConfiguration.java b/java/sdk/src/main/java/com/github/copilot/rpc/MemoryConfiguration.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/MemoryConfiguration.java rename to java/sdk/src/main/java/com/github/copilot/rpc/MemoryConfiguration.java diff --git a/java/src/main/java/com/github/copilot/rpc/MessageAttachment.java b/java/sdk/src/main/java/com/github/copilot/rpc/MessageAttachment.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/MessageAttachment.java rename to java/sdk/src/main/java/com/github/copilot/rpc/MessageAttachment.java diff --git a/java/src/main/java/com/github/copilot/rpc/MessageOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/MessageOptions.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/MessageOptions.java rename to java/sdk/src/main/java/com/github/copilot/rpc/MessageOptions.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelBilling.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelBilling.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelBilling.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelBilling.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelCapabilities.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelCapabilities.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelCapabilities.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelCapabilities.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelCapabilitiesOverride.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelCapabilitiesOverride.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelCapabilitiesOverride.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelCapabilitiesOverride.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelInfo.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelInfo.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelInfo.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelInfo.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelLimits.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelLimits.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelLimits.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelLimits.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelPolicy.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelPolicy.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelPolicy.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelPolicy.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelSupports.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelSupports.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelSupports.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelSupports.java diff --git a/java/src/main/java/com/github/copilot/rpc/ModelVisionLimits.java b/java/sdk/src/main/java/com/github/copilot/rpc/ModelVisionLimits.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ModelVisionLimits.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ModelVisionLimits.java diff --git a/java/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ParamCoercion.java b/java/sdk/src/main/java/com/github/copilot/rpc/ParamCoercion.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ParamCoercion.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ParamCoercion.java diff --git a/java/src/main/java/com/github/copilot/rpc/ParamSchema.java b/java/sdk/src/main/java/com/github/copilot/rpc/ParamSchema.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ParamSchema.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ParamSchema.java diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PermissionHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PermissionHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PermissionInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PermissionInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PermissionRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java diff --git a/java/src/main/java/com/github/copilot/rpc/PermissionRequestResultKind.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResultKind.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PermissionRequestResultKind.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResultKind.java diff --git a/java/src/main/java/com/github/copilot/rpc/PingResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/PingResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PingResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PingResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseFailureHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PostToolUseHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PostToolUseHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PostToolUseHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreMcpToolCallHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreToolUseHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreToolUseHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/PreToolUseHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/PreToolUseHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/PreToolUseHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/ProviderConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ProviderConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ProviderModelConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ProviderModelConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ProviderModelConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ProviderModelConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ProviderTokenArgs.java b/java/sdk/src/main/java/com/github/copilot/rpc/ProviderTokenArgs.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ProviderTokenArgs.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ProviderTokenArgs.java diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ResumeSessionResponse.java diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/RuntimeConnection.java b/java/sdk/src/main/java/com/github/copilot/rpc/RuntimeConnection.java new file mode 100644 index 000000000..a0c8eec5c --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/RuntimeConnection.java @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.github.copilot.CopilotExperimental; + +/** + * Configures how a {@link com.github.copilot.CopilotClient} connects to the + * Copilot runtime. + *

+ * Instances are created through the factory methods on this class and assigned + * with {@link CopilotClientOptions#setConnection(RuntimeConnection)}: + * + *

{@code
+ * // Spawn a runtime child process and talk over stdin/stdout (the default).
+ * new CopilotClientOptions().setConnection(RuntimeConnection.forStdio());
+ *
+ * // Spawn a runtime child process listening on a TCP socket.
+ * new CopilotClientOptions().setConnection(RuntimeConnection.forTcp().setPath("/usr/local/bin/copilot"));
+ *
+ * // Connect to an already-running runtime.
+ * new CopilotClientOptions().setConnection(RuntimeConnection.forUri("localhost:3000"));
+ * }
+ * + * @since 1.0.0 + */ +@CopilotExperimental +public abstract sealed class RuntimeConnection + permits StdioRuntimeConnection, TcpRuntimeConnection, UriRuntimeConnection, InProcessRuntimeConnection { + + RuntimeConnection() { + } + + /** + * Spawns a runtime child process and communicates over its stdin/stdout. This + * is the default when no connection is configured. + * + * @return a new stdio connection + */ + public static StdioRuntimeConnection forStdio() { + return new StdioRuntimeConnection(); + } + + /** + * Spawns a runtime child process at the given path and communicates over its + * stdin/stdout. + * + * @param path + * path to the runtime executable, or {@code null} to use the runtime + * discovered on the {@code PATH} + * @return a new stdio connection + */ + public static StdioRuntimeConnection forStdio(String path) { + return new StdioRuntimeConnection().setPath(path); + } + + /** + * Spawns a runtime child process that listens on a TCP socket and connects to + * it. + * + * @return a new TCP connection + */ + public static TcpRuntimeConnection forTcp() { + return new TcpRuntimeConnection(); + } + + /** + * Connects to an already-running runtime at the given URL. + * + * @param url + * URL of the runtime to connect to; accepts {@code "port"}, + * {@code "host:port"}, or a full URL + * @return a new URI connection + * @throws IllegalArgumentException + * if {@code url} is {@code null} or empty + */ + public static UriRuntimeConnection forUri(String url) { + return new UriRuntimeConnection(url); + } + + /** + * Hosts the runtime in-process by loading its native library and communicating + * over the C ABI — no child process is spawned by the SDK for JSON-RPC + * transport. + * + * @return a new in-process connection + */ + @CopilotExperimental + public static InProcessRuntimeConnection forInProcess() { + return new InProcessRuntimeConnection(); + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/SectionOverride.java b/java/sdk/src/main/java/com/github/copilot/rpc/SectionOverride.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SectionOverride.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SectionOverride.java diff --git a/java/src/main/java/com/github/copilot/rpc/SectionOverrideAction.java b/java/sdk/src/main/java/com/github/copilot/rpc/SectionOverrideAction.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SectionOverrideAction.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SectionOverrideAction.java diff --git a/java/src/main/java/com/github/copilot/rpc/SendMessageRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/SendMessageRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SendMessageRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SendMessageRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/SendMessageResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/SendMessageResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SendMessageResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SendMessageResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionCapabilities.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionCapabilities.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionCapabilities.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionCapabilities.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionContext.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionContext.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionContext.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionContext.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionEndHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionEndHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionEndHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionEndHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionEndHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionHooks.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionHooks.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionLifecycleEvent.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEvent.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionLifecycleEvent.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEvent.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionLifecycleEventMetadata.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEventMetadata.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionLifecycleEventMetadata.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEventMetadata.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionLifecycleEventTypes.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEventTypes.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionLifecycleEventTypes.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleEventTypes.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionLifecycleHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionLifecycleHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionLifecycleHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionListFilter.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionListFilter.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionListFilter.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionListFilter.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionMetadata.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionMetadata.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionMetadata.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionMetadata.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionStartHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionStartHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionStartHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionStartHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionStartHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionUiApi.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionUiApi.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionUiApi.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionUiApi.java diff --git a/java/src/main/java/com/github/copilot/rpc/SessionUiCapabilities.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionUiCapabilities.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SessionUiCapabilities.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SessionUiCapabilities.java diff --git a/java/src/main/java/com/github/copilot/rpc/SetForegroundSessionRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/SetForegroundSessionRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SetForegroundSessionRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SetForegroundSessionRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/SetForegroundSessionResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/SetForegroundSessionResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SetForegroundSessionResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SetForegroundSessionResponse.java diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/StdioRuntimeConnection.java b/java/sdk/src/main/java/com/github/copilot/rpc/StdioRuntimeConnection.java new file mode 100644 index 000000000..7d0923e0f --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/StdioRuntimeConnection.java @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.ArrayList; +import java.util.List; + +import com.github.copilot.CopilotExperimental; + +/** + * Spawns a runtime child process and communicates over its stdin/stdout. + * Construct with {@link RuntimeConnection#forStdio()} or + * {@link RuntimeConnection#forStdio(String)}. + * + * @since 1.0.0 + */ +@CopilotExperimental +public final class StdioRuntimeConnection extends RuntimeConnection { + + private String path; + private List args; + + StdioRuntimeConnection() { + } + + /** + * Returns the path to the runtime executable. + * + * @return the path, or {@code null} to use the runtime discovered on the + * {@code PATH} + */ + public String getPath() { + return path; + } + + /** + * Sets the path to the runtime executable. + * + * @param path + * the path, or {@code null} to use the runtime discovered on the + * {@code PATH} + * @return this instance for method chaining + */ + public StdioRuntimeConnection setPath(String path) { + this.path = path; + return this; + } + + /** + * Returns the extra command-line arguments passed to the runtime process. + * + * @return the arguments, or {@code null} if none are configured + */ + public List getArgs() { + return args; + } + + /** + * Sets extra command-line arguments passed to the runtime process. + * + * @param args + * the arguments, or {@code null} for none + * @return this instance for method chaining + */ + public StdioRuntimeConnection setArgs(List args) { + this.args = args == null ? null : new ArrayList<>(args); + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/SystemMessageSections.java b/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SystemMessageSections.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java diff --git a/java/src/main/java/com/github/copilot/rpc/SystemPromptSections.java b/java/sdk/src/main/java/com/github/copilot/rpc/SystemPromptSections.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/SystemPromptSections.java rename to java/sdk/src/main/java/com/github/copilot/rpc/SystemPromptSections.java diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/TcpRuntimeConnection.java b/java/sdk/src/main/java/com/github/copilot/rpc/TcpRuntimeConnection.java new file mode 100644 index 000000000..648321a21 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/TcpRuntimeConnection.java @@ -0,0 +1,116 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.ArrayList; +import java.util.List; + +import com.github.copilot.CopilotExperimental; + +/** + * Spawns a runtime child process listening on a TCP socket and connects to it. + * Construct with {@link RuntimeConnection#forTcp()}. + * + * @since 1.0.0 + */ +@CopilotExperimental +public final class TcpRuntimeConnection extends RuntimeConnection { + + private String path; + private int port; + private String connectionToken; + private List args; + + TcpRuntimeConnection() { + } + + /** + * Returns the path to the runtime executable. + * + * @return the path, or {@code null} to use the runtime discovered on the + * {@code PATH} + */ + public String getPath() { + return path; + } + + /** + * Sets the path to the runtime executable. + * + * @param path + * the path, or {@code null} to use the runtime discovered on the + * {@code PATH} + * @return this instance for method chaining + */ + public TcpRuntimeConnection setPath(String path) { + this.path = path; + return this; + } + + /** + * Returns the TCP port the spawned runtime listens on. + * + * @return the port, or {@code 0} to auto-allocate a free port + */ + public int getPort() { + return port; + } + + /** + * Sets the TCP port the spawned runtime listens on. + * + * @param port + * the port, or {@code 0} (the default) to auto-allocate a free port + * @return this instance for method chaining + */ + public TcpRuntimeConnection setPort(int port) { + this.port = port; + return this; + } + + /** + * Returns the shared secret the SDK sends to the spawned runtime to + * authenticate the TCP connection. + * + * @return the token, or {@code null} to generate one automatically + */ + public String getConnectionToken() { + return connectionToken; + } + + /** + * Sets the shared secret the SDK sends to the spawned runtime to authenticate + * the TCP connection. + * + * @param connectionToken + * the token, or {@code null} to generate one automatically + * @return this instance for method chaining + */ + public TcpRuntimeConnection setConnectionToken(String connectionToken) { + this.connectionToken = connectionToken; + return this; + } + + /** + * Returns the extra command-line arguments passed to the runtime process. + * + * @return the arguments, or {@code null} if none are configured + */ + public List getArgs() { + return args; + } + + /** + * Sets extra command-line arguments passed to the runtime process. + * + * @param args + * the arguments, or {@code null} for none + * @return this instance for method chaining + */ + public TcpRuntimeConnection setArgs(List args) { + this.args = args == null ? null : new ArrayList<>(args); + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/TelemetryConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/TelemetryConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/TelemetryConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/TelemetryConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolBinaryResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolBinaryResult.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolBinaryResult.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolBinaryResult.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolDefer.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolDefer.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolDefer.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolDefer.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolDefinition.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolDefinition.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolDefinition.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolDefinition.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolResultObject.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolResultObject.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolResultObject.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolResultObject.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolSearchConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolSearchConfig.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolSearchConfig.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolSearchConfig.java diff --git a/java/src/main/java/com/github/copilot/rpc/ToolSet.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolSet.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/ToolSet.java rename to java/sdk/src/main/java/com/github/copilot/rpc/ToolSet.java diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/UriRuntimeConnection.java b/java/sdk/src/main/java/com/github/copilot/rpc/UriRuntimeConnection.java new file mode 100644 index 000000000..c26098584 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/UriRuntimeConnection.java @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.github.copilot.CopilotExperimental; + +/** + * Connects to an already-running runtime at the configured URL. Construct with + * {@link RuntimeConnection#forUri(String)}. + * + * @since 1.0.0 + */ +@CopilotExperimental +public final class UriRuntimeConnection extends RuntimeConnection { + + private final String url; + private String connectionToken; + + UriRuntimeConnection(String url) { + if (url == null || url.isEmpty()) { + throw new IllegalArgumentException("UriRuntimeConnection url must be a non-empty string"); + } + this.url = url; + } + + /** + * Returns the URL of the runtime to connect to. + * + * @return the URL; accepts {@code "port"}, {@code "host:port"}, or a full URL + */ + public String getUrl() { + return url; + } + + /** + * Returns the shared secret used to authenticate the connection. + * + * @return the token, or {@code null} if the runtime does not require one + */ + public String getConnectionToken() { + return connectionToken; + } + + /** + * Sets the shared secret used to authenticate the connection. + * + * @param connectionToken + * the token, or {@code null} if the runtime does not require one + * @return this instance for method chaining + */ + public UriRuntimeConnection setConnectionToken(String connectionToken) { + this.connectionToken = connectionToken; + return this; + } +} diff --git a/java/src/main/java/com/github/copilot/rpc/UserInputHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserInputHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserInputHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserInputHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserInputInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserInputInvocation.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserInputInvocation.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserInputInvocation.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserInputRequest.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserInputRequest.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserInputRequest.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserInputRequest.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserInputResponse.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserInputResponse.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserInputResponse.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserInputResponse.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptSubmittedHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHandler.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookInput.java diff --git a/java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java rename to java/sdk/src/main/java/com/github/copilot/rpc/UserPromptTransformedHookOutput.java diff --git a/java/src/main/java/com/github/copilot/rpc/package-info.java b/java/sdk/src/main/java/com/github/copilot/rpc/package-info.java similarity index 100% rename from java/src/main/java/com/github/copilot/rpc/package-info.java rename to java/sdk/src/main/java/com/github/copilot/rpc/package-info.java diff --git a/java/src/main/java/com/github/copilot/tool/CopilotTool.java b/java/sdk/src/main/java/com/github/copilot/tool/CopilotTool.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/CopilotTool.java rename to java/sdk/src/main/java/com/github/copilot/tool/CopilotTool.java diff --git a/java/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java b/java/sdk/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java rename to java/sdk/src/main/java/com/github/copilot/tool/CopilotToolMetadataProvider.java diff --git a/java/src/main/java/com/github/copilot/tool/CopilotToolParam.java b/java/sdk/src/main/java/com/github/copilot/tool/CopilotToolParam.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/CopilotToolParam.java rename to java/sdk/src/main/java/com/github/copilot/tool/CopilotToolParam.java diff --git a/java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java b/java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java rename to java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java diff --git a/java/src/main/java/com/github/copilot/tool/Param.java b/java/sdk/src/main/java/com/github/copilot/tool/Param.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/Param.java rename to java/sdk/src/main/java/com/github/copilot/tool/Param.java diff --git a/java/src/main/java/com/github/copilot/tool/SchemaGenerator.java b/java/sdk/src/main/java/com/github/copilot/tool/SchemaGenerator.java similarity index 100% rename from java/src/main/java/com/github/copilot/tool/SchemaGenerator.java rename to java/sdk/src/main/java/com/github/copilot/tool/SchemaGenerator.java diff --git a/java/src/main/java/module-info.java b/java/sdk/src/main/java/module-info.java similarity index 94% rename from java/src/main/java/module-info.java rename to java/sdk/src/main/java/module-info.java index 38bc1f93d..8bc2dbd55 100644 --- a/java/src/main/java/module-info.java +++ b/java/sdk/src/main/java/module-info.java @@ -12,6 +12,7 @@ requires com.fasterxml.jackson.datatype.jsr310; requires static com.github.spotbugs.annotations; requires static java.compiler; + requires static com.sun.jna; requires java.net.http; requires java.logging; @@ -25,6 +26,7 @@ opens com.github.copilot.generated to com.fasterxml.jackson.databind; opens com.github.copilot.generated.rpc to com.fasterxml.jackson.databind; opens com.github.copilot.rpc to com.fasterxml.jackson.databind; + opens com.github.copilot.ffi to com.sun.jna; provides javax.annotation.processing.Processor with com.github.copilot.CopilotExperimentalProcessor, com.github.copilot.tool.CopilotToolProcessor; diff --git a/java/src/main/java25/com/github/copilot/InternalExecutorProvider.java b/java/sdk/src/main/java25/com/github/copilot/InternalExecutorProvider.java similarity index 100% rename from java/src/main/java25/com/github/copilot/InternalExecutorProvider.java rename to java/sdk/src/main/java25/com/github/copilot/InternalExecutorProvider.java diff --git a/java/sdk/src/main/java25/com/github/copilot/ffi/ReaderThreadFactory.java b/java/sdk/src/main/java25/com/github/copilot/ffi/ReaderThreadFactory.java new file mode 100644 index 000000000..a67346b88 --- /dev/null +++ b/java/sdk/src/main/java25/com/github/copilot/ffi/ReaderThreadFactory.java @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +/** + * JDK 25 multi-release variant of {@link ReaderThreadFactory}. + */ +final class ReaderThreadFactory { + + Thread create(Runnable task, String name) { + return Thread.ofVirtual().name(name).unstarted(task); + } +} diff --git a/java/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/java/sdk/src/main/resources/META-INF/services/javax.annotation.processing.Processor similarity index 100% rename from java/src/main/resources/META-INF/services/javax.annotation.processing.Processor rename to java/sdk/src/main/resources/META-INF/services/javax.annotation.processing.Processor diff --git a/java/sdk/src/main/resources/copilot-runtime.properties b/java/sdk/src/main/resources/copilot-runtime.properties new file mode 100644 index 000000000..290046444 --- /dev/null +++ b/java/sdk/src/main/resources/copilot-runtime.properties @@ -0,0 +1,3 @@ +# This file is processed by Maven resource filtering. +# The ${project.version} placeholder is replaced at build time. +version=${project.version} diff --git a/java/src/test/java/com/github/copilot/AgentInfoTest.java b/java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/AgentInfoTest.java rename to java/sdk/src/test/java/com/github/copilot/AgentInfoTest.java diff --git a/java/src/test/java/com/github/copilot/AgentModeTest.java b/java/sdk/src/test/java/com/github/copilot/AgentModeTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/AgentModeTest.java rename to java/sdk/src/test/java/com/github/copilot/AgentModeTest.java diff --git a/java/src/test/java/com/github/copilot/AskUserTest.java b/java/sdk/src/test/java/com/github/copilot/AskUserTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/AskUserTest.java rename to java/sdk/src/test/java/com/github/copilot/AskUserTest.java diff --git a/java/src/test/java/com/github/copilot/ByokBearerTokenProviderE2ETest.java b/java/sdk/src/test/java/com/github/copilot/ByokBearerTokenProviderE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ByokBearerTokenProviderE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/ByokBearerTokenProviderE2ETest.java diff --git a/java/src/test/java/com/github/copilot/CapiProxy.java b/java/sdk/src/test/java/com/github/copilot/CapiProxy.java similarity index 95% rename from java/src/test/java/com/github/copilot/CapiProxy.java rename to java/sdk/src/test/java/com/github/copilot/CapiProxy.java index 6484f0581..53d5e1166 100644 --- a/java/src/test/java/com/github/copilot/CapiProxy.java +++ b/java/sdk/src/test/java/com/github/copilot/CapiProxy.java @@ -93,9 +93,10 @@ public String start() throws IOException, InterruptedException { // Start the harness server using npx tsx // On Windows, npx is installed as npx.cmd which requires cmd /c to launch boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); + String npxPath = resolveCommand(isWindows ? "npx.cmd" : "npx"); var pb = isWindows - ? new ProcessBuilder("cmd", "/c", "npx", "tsx", "server.ts") - : new ProcessBuilder("npx", "tsx", "server.ts"); + ? new ProcessBuilder(System.getenv("COMSPEC"), "/c", npxPath, "tsx", "server.ts") + : new ProcessBuilder(npxPath, "tsx", "server.ts"); pb.directory(harnessDir.toFile()); pb.redirectErrorStream(false); // Tell the replaying proxy to fail fast on unmatched requests rather than @@ -516,6 +517,24 @@ private Path findHarnessDirectory() { return null; } + /** + * Resolves a command name to its absolute path by searching the system + * {@code PATH}. Falls back to the original name if not found. + */ + private static String resolveCommand(String command) { + String pathEnv = System.getenv("PATH"); + if (pathEnv == null) { + return command; + } + for (String dir : pathEnv.split(java.io.File.pathSeparator)) { + Path candidate = Path.of(dir, command); + if (java.nio.file.Files.isExecutable(candidate)) { + return candidate.toAbsolutePath().toString(); + } + } + return command; + } + /** * Test information record for configuring the proxy. */ diff --git a/java/src/test/java/com/github/copilot/CapiSessionOptionsTest.java b/java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CapiSessionOptionsTest.java rename to java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java diff --git a/java/src/test/java/com/github/copilot/CliServerManagerTest.java b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java similarity index 98% rename from java/src/test/java/com/github/copilot/CliServerManagerTest.java rename to java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java index b445d6153..353858135 100644 --- a/java/src/test/java/com/github/copilot/CliServerManagerTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java @@ -72,7 +72,9 @@ void connectToServerTcpMode() throws Exception { private static Process startBlockingProcess() throws IOException { boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); - return (isWindows ? new ProcessBuilder("cmd", "/c", "more") : new ProcessBuilder("cat")).start(); + return (isWindows + ? new ProcessBuilder(System.getenv("COMSPEC"), "/c", "more") + : new ProcessBuilder("/usr/bin/cat")).start(); } @Test diff --git a/java/src/test/java/com/github/copilot/ClientOptionsE2ETest.java b/java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ClientOptionsE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/ClientOptionsE2ETest.java diff --git a/java/src/test/java/com/github/copilot/ClosedSessionGuardTest.java b/java/sdk/src/test/java/com/github/copilot/ClosedSessionGuardTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ClosedSessionGuardTest.java rename to java/sdk/src/test/java/com/github/copilot/ClosedSessionGuardTest.java diff --git a/java/src/test/java/com/github/copilot/CommandsTest.java b/java/sdk/src/test/java/com/github/copilot/CommandsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CommandsTest.java rename to java/sdk/src/test/java/com/github/copilot/CommandsTest.java diff --git a/java/src/test/java/com/github/copilot/CompactionTest.java b/java/sdk/src/test/java/com/github/copilot/CompactionTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CompactionTest.java rename to java/sdk/src/test/java/com/github/copilot/CompactionTest.java diff --git a/java/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java similarity index 99% rename from java/src/test/java/com/github/copilot/ConfigCloneTest.java rename to java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java index b2b744eed..4c5a3fbef 100644 --- a/java/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -385,6 +385,15 @@ void copilotClientOptionsSetEnvironmentNullClearsExisting() { assertTrue(env == null || env.isEmpty()); } + @Test + void copilotClientOptionsSetCwdNullClearsExisting() { + CopilotClientOptions opts = new CopilotClientOptions().setCwd("/tmp"); + + opts.setCwd(null); + + assertNull(opts.getCwd()); + } + @Test @SuppressWarnings("deprecation") void copilotClientOptionsDeprecatedGithubToken() { diff --git a/java/src/test/java/com/github/copilot/CopilotClientModeTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientModeTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotClientModeTest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotClientModeTest.java diff --git a/java/src/test/java/com/github/copilot/CopilotClientTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java similarity index 99% rename from java/src/test/java/com/github/copilot/CopilotClientTest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java index d977563ae..067571df1 100644 --- a/java/src/test/java/com/github/copilot/CopilotClientTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java @@ -610,9 +610,9 @@ void testListModels_WithCustomHandler_WorksWithoutStart() throws Exception { private static void setConnectionFuture(CopilotClient client, JsonRpcClient rpc, Process process) throws Exception { var connectionClass = Class.forName("com.github.copilot.CopilotClient$Connection"); var constructor = connectionClass.getDeclaredConstructor(JsonRpcClient.class, Process.class, - com.github.copilot.generated.rpc.ServerRpc.class); + com.github.copilot.generated.rpc.ServerRpc.class, AutoCloseable.class); constructor.setAccessible(true); - var connection = constructor.newInstance(rpc, process, null); + var connection = constructor.newInstance(rpc, process, null, null); Field field = CopilotClient.class.getDeclaredField("connectionFuture"); field.setAccessible(true); diff --git a/java/sdk/src/test/java/com/github/copilot/CopilotClientTransportTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientTransportTest.java new file mode 100644 index 000000000..46223d56d --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/CopilotClientTransportTest.java @@ -0,0 +1,387 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.Pipe; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.InProcessRuntimeConnection; +import com.github.copilot.rpc.RuntimeConnection; +import com.github.copilot.rpc.StdioRuntimeConnection; +import com.github.copilot.rpc.TcpRuntimeConnection; +import com.github.copilot.rpc.TelemetryConfig; +import com.github.copilot.rpc.UriRuntimeConnection; + +/** + * Unit tests for transport selection through {@link RuntimeConnection}: the + * in-process code path, {@code COPILOT_SDK_DEFAULT_CONNECTION} resolution, the + * backward-compatibility bridge from the individual transport options, and + * option validation. + */ +@AllowCopilotExperimental +class CopilotClientTransportTest { + + // ===== In-process routing ===== + + @Test + void inProcessConnectionStartsThroughInProcessRuntimeHost() throws Exception { + var options = new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()); + try (var runtime = new FakeInProcessRuntime(); var client = new CopilotClient(options)) { + client.setInProcessTransportFactory(runtime::open); + + client.start().get(30, TimeUnit.SECONDS); + + assertTrue(runtime.opened.get(), "The in-process runtime must be used for an in-process connection"); + assertInstanceOf(InProcessRuntimeConnection.class, client.getRuntimeConnection()); + + client.stop().get(30, TimeUnit.SECONDS); + assertTrue(runtime.closed.get(), "Stopping the client must close the in-process runtime host"); + } + } + + @Test + void inProcessStartupFailurePropagates() { + var options = new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()); + try (var client = new CopilotClient(options)) { + client.setInProcessTransportFactory(opts -> { + throw new IOException("no runtime available"); + }); + var failure = assertThrows(Exception.class, () -> client.start().get(30, TimeUnit.SECONDS)); + assertTrue(rootMessage(failure).contains("no runtime available")); + } + } + + @Test + void cliTransportDoesNotUseTheInProcessRuntime() throws Exception { + var options = new CopilotClientOptions().setCliUrl("127.0.0.1:1"); + try (var client = new CopilotClient(options)) { + client.setInProcessTransportFactory(opts -> { + throw new AssertionError("The in-process runtime must not be used for a CLI transport"); + }); + + assertThrows(Exception.class, () -> client.start().get(30, TimeUnit.SECONDS)); + assertInstanceOf(UriRuntimeConnection.class, client.getRuntimeConnection()); + } + } + + // ===== COPILOT_SDK_DEFAULT_CONNECTION resolution ===== + + @Test + void defaultConnectionEnvVarSelectsInProcess() { + var connection = CopilotClient.resolveDefaultConnection(new CopilotClientOptions(), "inprocess"); + assertInstanceOf(InProcessRuntimeConnection.class, connection); + assertInstanceOf(InProcessRuntimeConnection.class, + CopilotClient.resolveDefaultConnection(new CopilotClientOptions(), "InProcess")); + } + + @Test + void defaultConnectionEnvVarStdioAndUnsetKeepTheConfiguredTransport() { + assertInstanceOf(StdioRuntimeConnection.class, + CopilotClient.resolveDefaultConnection(new CopilotClientOptions(), "stdio")); + assertInstanceOf(StdioRuntimeConnection.class, + CopilotClient.resolveDefaultConnection(new CopilotClientOptions(), null)); + assertInstanceOf(TcpRuntimeConnection.class, + CopilotClient.resolveDefaultConnection(new CopilotClientOptions().setUseStdio(false), "")); + assertInstanceOf(TcpRuntimeConnection.class, CopilotClient.resolveDefaultConnection( + new CopilotClientOptions().setUseStdio(false).setTcpConnectionToken("secret"), "inprocess")); + } + + @Test + void defaultConnectionEnvVarRejectsUnknownValues() { + var error = assertThrows(IllegalArgumentException.class, + () -> CopilotClient.resolveDefaultConnection(new CopilotClientOptions(), "websocket")); + assertTrue(error.getMessage().contains(CopilotClient.DEFAULT_CONNECTION_ENV_VAR)); + } + + // ===== Backward-compatibility bridge ===== + + @Test + void legacyStdioOptionsInferStdioConnection() { + try (var client = new CopilotClient(new CopilotClientOptions().setCliPath("/usr/local/bin/copilot"))) { + var connection = assertInstanceOf(StdioRuntimeConnection.class, client.getRuntimeConnection()); + assertEquals("/usr/local/bin/copilot", connection.getPath()); + } + } + + @Test + void legacyTcpOptionsInferTcpConnection() { + var options = new CopilotClientOptions().setUseStdio(false).setPort(4321).setTcpConnectionToken("secret"); + try (var client = new CopilotClient(options)) { + var connection = assertInstanceOf(TcpRuntimeConnection.class, client.getRuntimeConnection()); + assertEquals(4321, connection.getPort()); + assertEquals("secret", connection.getConnectionToken()); + } + } + + @Test + void legacyCliUrlInfersUriConnection() { + try (var client = new CopilotClient(new CopilotClientOptions().setCliUrl("localhost:3000"))) { + var connection = assertInstanceOf(UriRuntimeConnection.class, client.getRuntimeConnection()); + assertEquals("localhost:3000", connection.getUrl()); + } + } + + // ===== Connection applied to the transport options ===== + + @Test + void connectionIsProjectedOntoTransportOptions() { + var stdio = new CopilotClientOptions().setConnection(RuntimeConnection.forStdio("/opt/copilot")); + try (var client = new CopilotClient(stdio)) { + assertTrue(stdio.isUseStdio()); + assertEquals("/opt/copilot", stdio.getCliPath()); + } + + var tcp = new CopilotClientOptions().setConnection( + RuntimeConnection.forTcp().setPort(4321).setConnectionToken("secret").setArgs(List.of("--extra"))); + try (var client = new CopilotClient(tcp)) { + assertFalse(tcp.isUseStdio()); + assertEquals(4321, tcp.getPort()); + assertEquals("secret", tcp.getTcpConnectionToken()); + assertEquals(List.of("--extra"), List.of(tcp.getCliArgs())); + } + + var uri = new CopilotClientOptions().setConnection(RuntimeConnection.forUri("localhost:3000")); + try (var client = new CopilotClient(uri)) { + assertFalse(uri.isUseStdio()); + assertEquals("localhost:3000", uri.getCliUrl()); + } + } + + // ===== Conflicting configuration ===== + + @Test + void connectionCannotBeCombinedWithTransportOptions() { + assertConflict(new CopilotClientOptions().setConnection(RuntimeConnection.forStdio()) + .setCliPath("/usr/local/bin/copilot"), "CliPath"); + assertConflict( + new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()).setCliUrl("localhost:3000"), + "CliUrl"); + assertConflict(new CopilotClientOptions().setConnection(RuntimeConnection.forStdio()).setUseStdio(false), + "UseStdio"); + assertConflict(new CopilotClientOptions().setConnection(RuntimeConnection.forTcp()).setPort(4321), "Port"); + assertConflict( + new CopilotClientOptions().setConnection(RuntimeConnection.forTcp()).setTcpConnectionToken("secret"), + "TcpConnectionToken"); + assertConflict(new CopilotClientOptions().setConnection(RuntimeConnection.forStdio()) + .setCliArgs(new String[]{"--extra"}), "CliArgs"); + } + + @Test + void connectionCanBeReusedForSeveralClients() { + var options = new CopilotClientOptions().setConnection(RuntimeConnection.forStdio("/opt/copilot")); + try (var first = new CopilotClient(options); var second = new CopilotClient(options)) { + assertInstanceOf(StdioRuntimeConnection.class, first.getRuntimeConnection()); + assertInstanceOf(StdioRuntimeConnection.class, second.getRuntimeConnection()); + } + } + + private static void assertConflict(CopilotClientOptions options, String optionName) { + var error = assertThrows(IllegalArgumentException.class, () -> new CopilotClient(options)); + assertTrue(error.getMessage().contains(optionName), "Expected '" + optionName + "' in: " + error.getMessage()); + } + + // ===== Options rejected for the in-process transport ===== + + @Test + void inProcessRejectsPerProcessOptions() { + assertInProcessRejected(new CopilotClientOptions().setEnvironment(Map.of("FOO", "bar")), "Environment"); + assertInProcessRejected(new CopilotClientOptions().setTelemetry(new TelemetryConfig()), "Telemetry"); + assertInProcessRejected(new CopilotClientOptions().setCwd("/tmp"), "Cwd"); + assertInProcessRejected(new CopilotClientOptions().setCliArgs(new String[]{"--extra"}), "CliArgs"); + } + + @Test + void e2eContextClearsInProcessIncompatibleOptions() throws Exception { + try (var context = E2ETestContext.create()) { + var options = new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()) + .setEnvironment(Map.of("TEST_KEY", "test-value")).setCwd(context.getWorkDir().toString()) + .setCliArgs(new String[]{"--subprocess-only"}); + + try (var client = context.createClient(options)) { + assertInstanceOf(InProcessRuntimeConnection.class, client.getRuntimeConnection()); + assertTrue(options.getEnvironment() == null || options.getEnvironment().isEmpty()); + assertEquals(null, options.getCwd()); + assertTrue(options.getCliArgs() == null || options.getCliArgs().length == 0); + } + } + } + + private static void assertInProcessRejected(CopilotClientOptions options, String optionName) { + options.setConnection(RuntimeConnection.forInProcess()); + var error = assertThrows(IllegalArgumentException.class, () -> new CopilotClient(options)); + assertTrue(error.getMessage().contains(optionName), "Expected '" + optionName + "' in: " + error.getMessage()); + assertTrue(error.getMessage().contains("forInProcess"), + "Expected the in-process transport to be named in: " + error.getMessage()); + } + + private static String rootMessage(Throwable error) { + Throwable cause = error; + while (cause.getCause() != null) { + cause = cause.getCause(); + } + return String.valueOf(cause.getMessage()); + } + + /** + * Minimal loopback stand-in for the in-process runtime: it speaks just enough + * JSON-RPC for {@link CopilotClient#start()} to complete, so the test can + * assert that the client wires its transport to the in-process host rather than + * to a child process. + */ + private static final class FakeInProcessRuntime implements AutoCloseable { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private final AtomicBoolean opened = new AtomicBoolean(); + private final AtomicBoolean closed = new AtomicBoolean(); + private final BytePipe toClient; + private final BytePipe toRuntime; + private final InputStream runtimeInput; + private final OutputStream runtimeOutput; + private final Thread responder; + + FakeInProcessRuntime() throws IOException { + this.toClient = new BytePipe(); + this.toRuntime = new BytePipe(); + this.runtimeInput = toRuntime.inputStream(); + this.runtimeOutput = toClient.outputStream(); + this.responder = new Thread(this::respondToRequests, "fake-inprocess-runtime"); + this.responder.setDaemon(true); + this.responder.start(); + } + + CopilotClient.InProcessTransport open(CopilotClientOptions options) { + opened.set(true); + return new CopilotClient.InProcessTransport(toClient.inputStream(), toRuntime.outputStream(), this::close); + } + + @Override + public void close() { + if (!closed.compareAndSet(false, true)) { + return; + } + toRuntime.close(); + toClient.close(); + } + + private void respondToRequests() { + try { + while (!closed.get()) { + JsonNode request = readMessage(runtimeInput); + if (request == null) { + return; + } + if (!request.hasNonNull("id")) { + continue; + } + var response = MAPPER.createObjectNode(); + response.put("jsonrpc", "2.0"); + response.set("id", request.get("id")); + var result = response.putObject("result"); + if ("connect".equals(request.path("method").asText())) { + result.put("protocolVersion", SdkProtocolVersion.get()); + } + writeMessage(runtimeOutput, response); + } + } catch (IOException e) { + // The streams are closed when the client shuts down. + } + } + + private static JsonNode readMessage(InputStream in) throws IOException { + int contentLength = -1; + var line = new ByteArrayOutputStream(); + while (true) { + int b = in.read(); + if (b == -1) { + return null; + } + if (b == '\n') { + String header = line.toString(StandardCharsets.UTF_8).trim(); + line.reset(); + if (header.isEmpty()) { + break; + } + if (header.toLowerCase(Locale.ROOT).startsWith("content-length:")) { + contentLength = Integer.parseInt(header.substring(header.indexOf(':') + 1).trim()); + } + } else if (b != '\r') { + line.write(b); + } + } + if (contentLength < 0) { + throw new IOException("Missing Content-Length header"); + } + byte[] body = in.readNBytes(contentLength); + if (body.length != contentLength) { + return null; + } + return MAPPER.readTree(body); + } + + private static void writeMessage(OutputStream out, JsonNode message) throws IOException { + byte[] body = MAPPER.writeValueAsBytes(message); + out.write(("Content-Length: " + body.length + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)); + out.write(body); + out.flush(); + } + } + + /** + * Duplex byte channel used by {@link FakeInProcessRuntime} to emulate the + * streams of an in-process runtime. + */ + private static final class BytePipe { + + private final Pipe pipe; + + BytePipe() throws IOException { + this.pipe = Pipe.open(); + } + + InputStream inputStream() { + return Channels.newInputStream(pipe.source()); + } + + OutputStream outputStream() { + return Channels.newOutputStream(pipe.sink()); + } + + void close() { + closeQuietly(pipe.sink()); + closeQuietly(pipe.source()); + } + + private static void closeQuietly(Closeable closeable) { + try { + closeable.close(); + } catch (IOException e) { + // Nothing useful to do while tearing down a test pipe. + } + } + } +} diff --git a/java/src/test/java/com/github/copilot/CopilotExperimentalProcessorTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotExperimentalProcessorTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotExperimentalProcessorTest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotExperimentalProcessorTest.java diff --git a/java/src/test/java/com/github/copilot/CopilotRequestCancelErrorE2ETest.java b/java/sdk/src/test/java/com/github/copilot/CopilotRequestCancelErrorE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotRequestCancelErrorE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotRequestCancelErrorE2ETest.java diff --git a/java/src/test/java/com/github/copilot/CopilotRequestHandlerE2ETest.java b/java/sdk/src/test/java/com/github/copilot/CopilotRequestHandlerE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotRequestHandlerE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotRequestHandlerE2ETest.java diff --git a/java/src/test/java/com/github/copilot/CopilotRequestSessionIdE2ETest.java b/java/sdk/src/test/java/com/github/copilot/CopilotRequestSessionIdE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotRequestSessionIdE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotRequestSessionIdE2ETest.java diff --git a/java/src/test/java/com/github/copilot/CopilotRequestTestSupport.java b/java/sdk/src/test/java/com/github/copilot/CopilotRequestTestSupport.java similarity index 99% rename from java/src/test/java/com/github/copilot/CopilotRequestTestSupport.java rename to java/sdk/src/test/java/com/github/copilot/CopilotRequestTestSupport.java index ecbf92068..aa173ef30 100644 --- a/java/src/test/java/com/github/copilot/CopilotRequestTestSupport.java +++ b/java/sdk/src/test/java/com/github/copilot/CopilotRequestTestSupport.java @@ -72,7 +72,8 @@ static CopilotClient newLlmClient(E2ETestContext ctx, CopilotRequestHandler hand env.put(entry.substring(0, eq), entry.substring(eq + 1)); } } - return ctx.createClient(new CopilotClientOptions().setEnvironment(env).setRequestHandler(handler)); + return ctx.createClient( + new CopilotClientOptions().setCliPath(ctx.getCliPath()).setEnvironment(env).setRequestHandler(handler)); } /** diff --git a/java/src/test/java/com/github/copilot/CopilotSessionTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/CopilotSessionTest.java rename to java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java diff --git a/java/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java b/java/sdk/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java similarity index 99% rename from java/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java rename to java/sdk/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java index 156c96848..79e968cd3 100644 --- a/java/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CreateSessionReKeyEntryTest.java @@ -170,8 +170,9 @@ private static void injectConnection(CopilotClient client, JsonRpcClient rpc) th var ctor = connClass.getDeclaredConstructors()[0]; ctor.setAccessible(true); - // Connection(JsonRpcClient rpc, Process process, ServerRpc serverRpc) - Object connection = ctor.newInstance(rpc, null, null); + // Connection(JsonRpcClient rpc, Process process, ServerRpc serverRpc, + // AutoCloseable runtimeHost) + Object connection = ctor.newInstance(rpc, null, null, null); Field f = CopilotClient.class.getDeclaredField("connectionFuture"); f.setAccessible(true); diff --git a/java/src/test/java/com/github/copilot/DataObjectCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/DataObjectCoverageTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/DataObjectCoverageTest.java rename to java/sdk/src/test/java/com/github/copilot/DataObjectCoverageTest.java diff --git a/java/src/test/java/com/github/copilot/DocumentationSamplesTest.java b/java/sdk/src/test/java/com/github/copilot/DocumentationSamplesTest.java similarity index 99% rename from java/src/test/java/com/github/copilot/DocumentationSamplesTest.java rename to java/sdk/src/test/java/com/github/copilot/DocumentationSamplesTest.java index f7170f4fd..4e1396aa9 100644 --- a/java/src/test/java/com/github/copilot/DocumentationSamplesTest.java +++ b/java/sdk/src/test/java/com/github/copilot/DocumentationSamplesTest.java @@ -132,7 +132,7 @@ private static String stripStringsAndComments(String input) { private static List documentationFiles() throws IOException { Path root = Path.of("").toAbsolutePath(); List files = new ArrayList<>(); - files.add(root.resolve("README.md")); + files.add(root.resolve("../README.md")); files.add(root.resolve("jbang-example.java")); return files; } diff --git a/java/src/test/java/com/github/copilot/E2ETestContext.java b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java similarity index 90% rename from java/src/test/java/com/github/copilot/E2ETestContext.java rename to java/sdk/src/test/java/com/github/copilot/E2ETestContext.java index f524b33da..60dcf1fa3 100644 --- a/java/src/test/java/com/github/copilot/E2ETestContext.java +++ b/java/sdk/src/test/java/com/github/copilot/E2ETestContext.java @@ -18,7 +18,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.github.copilot.ffi.InProcessEnvGuard; import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.InProcessRuntimeConnection; +import com.github.copilot.rpc.RuntimeConnection; /** * E2E test context that manages the test environment including the CapiProxy, @@ -71,6 +74,7 @@ public class E2ETestContext implements AutoCloseable { private String proxyUrl; private final CapiProxy proxy; private final Path repoRoot; + private final List inProcessEnvGuards = new ArrayList<>(); private Path currentSnapshotFile; private E2ETestContext(String cliPath, Path homeDir, Path workDir, String proxyUrl, CapiProxy proxy, @@ -322,10 +326,8 @@ public Map getEnvironment() { * @return a new CopilotClient */ public CopilotClient createClient() { - CopilotClientOptions options = new CopilotClientOptions().setCliPath(cliPath).setCwd(workDir.toString()) - .setEnvironment(getEnvironment()).setGitHubToken(DEFAULT_GITHUB_TOKEN); - - return new CopilotClient(options); + CopilotClientOptions options = new CopilotClientOptions().setGitHubToken(DEFAULT_GITHUB_TOKEN); + return createClient(options); } /** @@ -338,6 +340,31 @@ public CopilotClient createClient() { * @return a new CopilotClient */ public CopilotClient createClient(CopilotClientOptions options) { + CopilotClient client = applyContextOptions(options); + if (client != null) { + return client; + } + if (options.getGitHubToken() == null) { + options.setGitHubToken(DEFAULT_GITHUB_TOKEN); + } + + return new CopilotClient(options); + } + + private CopilotClient applyContextOptions(CopilotClientOptions options) { + if (isInProcessMode(options)) { + InProcessEnvGuard guard = new InProcessEnvGuard(buildInProcessEnvironment(options)); + inProcessEnvGuards.add(guard); + try { + options.setEnvironment(null); + options.setCwd(null); + options.setCliArgs(null); + return new CopilotClient(options, guard::close); + } catch (RuntimeException e) { + guard.close(); + throw e; + } + } if (options.getCliPath() == null) { options.setCliPath(cliPath); } @@ -347,11 +374,30 @@ public CopilotClient createClient(CopilotClientOptions options) { if (options.getEnvironment() == null || options.getEnvironment().isEmpty()) { options.setEnvironment(getEnvironment()); } - if (options.getGitHubToken() == null) { - options.setGitHubToken(DEFAULT_GITHUB_TOKEN); + return null; + } + + private boolean isInProcessMode(CopilotClientOptions options) { + RuntimeConnection connection = options.getConnection(); + if (connection != null) { + return connection instanceof InProcessRuntimeConnection; + } + if (options.getRequestHandler() != null || options.getCliUrl() != null || options.getCliPath() != null + || options.getPort() != 0) { + return false; } + String defaultConnection = System.getenv("COPILOT_SDK_DEFAULT_CONNECTION"); + return defaultConnection != null && "inprocess".equalsIgnoreCase(defaultConnection.trim()); + } - return new CopilotClient(options); + private Map buildInProcessEnvironment(CopilotClientOptions options) { + Map env = new HashMap<>(getEnvironment()); + Map optionEnvironment = options.getEnvironment(); + if (optionEnvironment != null && !optionEnvironment.isEmpty()) { + env.putAll(optionEnvironment); + options.setEnvironment(null); + } + return env; } /** @@ -428,6 +474,9 @@ public void initializeProxy() throws IOException, InterruptedException { @Override public void close() throws Exception { + for (int i = inProcessEnvGuards.size() - 1; i >= 0; i--) { + inProcessEnvGuards.get(i).close(); + } proxy.stop(); // Clean up temp directories (best effort) diff --git a/java/src/test/java/com/github/copilot/ElicitationTest.java b/java/sdk/src/test/java/com/github/copilot/ElicitationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ElicitationTest.java rename to java/sdk/src/test/java/com/github/copilot/ElicitationTest.java diff --git a/java/src/test/java/com/github/copilot/ErrorHandlingTest.java b/java/sdk/src/test/java/com/github/copilot/ErrorHandlingTest.java similarity index 94% rename from java/src/test/java/com/github/copilot/ErrorHandlingTest.java rename to java/sdk/src/test/java/com/github/copilot/ErrorHandlingTest.java index 32579ffc4..46f6741a0 100644 --- a/java/src/test/java/com/github/copilot/ErrorHandlingTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ErrorHandlingTest.java @@ -158,6 +158,10 @@ void testShouldHandlePermissionHandlerErrorsGracefully_deniesPermission() throws || content.contains("permission") || content.contains("denied"), "Response should indicate permission was denied: " + content); + // Verify that the error handler was wired correctly. Whether error events are + // actually emitted depends on the CLI version and the scenario's replay data. + LOG.info("Collected " + errorEvents.size() + " error event(s) from permission handler crash"); + session.close(); } } @@ -198,9 +202,10 @@ void testPermissionHandlerErrors_sessionErrorEventContainsDetails() throws Excep session.close(); } - // Note: Whether error events are emitted depends on the CLI version and - // scenario - // This test verifies the handler can receive them when they occur + // Whether error events are emitted depends on the CLI version and scenario. + // This test verifies the handler can receive them when they occur. + // Access the list to confirm it was populated (even if empty is acceptable). + LOG.info("Collected " + errorEvents.size() + " error event(s)"); } /** diff --git a/java/src/test/java/com/github/copilot/EventFidelityTest.java b/java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/EventFidelityTest.java rename to java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java diff --git a/java/src/test/java/com/github/copilot/ExecutorWiringTest.java b/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java similarity index 95% rename from java/src/test/java/com/github/copilot/ExecutorWiringTest.java rename to java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java index 78764db0f..a8319475c 100644 --- a/java/src/test/java/com/github/copilot/ExecutorWiringTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ExecutorWiringTest.java @@ -86,8 +86,7 @@ int getTaskCount() { } private CopilotClientOptions createOptionsWithExecutor(TrackingExecutor executor) { - CopilotClientOptions options = new CopilotClientOptions().setCliPath(ctx.getCliPath()) - .setCwd(ctx.getWorkDir().toString()).setEnvironment(ctx.getEnvironment()).setExecutor(executor) + CopilotClientOptions options = new CopilotClientOptions().setExecutor(executor) .setGitHubToken("fake-token-for-e2e-tests"); return options; } @@ -111,7 +110,7 @@ void testClientStartUsesProvidedExecutor() throws Exception { TrackingExecutor trackingExecutor = new TrackingExecutor(ForkJoinPool.commonPool()); int beforeStart = trackingExecutor.getTaskCount(); - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { + try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) { client.start().get(30, TimeUnit.SECONDS); assertTrue(trackingExecutor.getTaskCount() > beforeStart, @@ -156,7 +155,7 @@ void testToolCallDispatchUsesProvidedExecutor() throws Exception { }); // Reset count after client construction to isolate tool-call dispatch - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { + try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) { CopilotSession session = client.createSession(new SessionConfig().setTools(List.of(encryptTool)) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); @@ -198,7 +197,7 @@ void testPermissionDispatchUsesProvidedExecutor() throws Exception { var config = new SessionConfig().setOnPermissionRequest((request, invocation) -> CompletableFuture .completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.APPROVED))); - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { + try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) { CopilotSession session = client.createSession(config).get(); Path testFile = ctx.getWorkDir().resolve("test.txt"); @@ -247,7 +246,7 @@ void testUserInputDispatchUsesProvidedExecutor() throws Exception { .completedFuture(new UserInputResponse().setAnswer(answer).setWasFreeform(wasFreeform)); }); - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { + try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) { CopilotSession session = client.createSession(config).get(); int beforeSend = trackingExecutor.getTaskCount(); @@ -286,7 +285,7 @@ void testHooksDispatchUsesProvidedExecutor() throws Exception { .setHooks(new SessionHooks().setOnPreToolUse( (input, invocation) -> CompletableFuture.completedFuture(PreToolUseHookOutput.allow()))); - try (CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor))) { + try (CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor))) { CopilotSession session = client.createSession(config).get(); Path testFile = ctx.getWorkDir().resolve("hello.txt"); @@ -342,7 +341,7 @@ void testClientStopUsesProvidedExecutor() throws Exception { return CompletableFuture.completedFuture(input.toUpperCase()); }); - CopilotClient client = new CopilotClient(createOptionsWithExecutor(trackingExecutor)); + CopilotClient client = ctx.createClient(createOptionsWithExecutor(trackingExecutor)); client.createSession(new SessionConfig().setTools(List.of(encryptTool)) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); diff --git a/java/src/test/java/com/github/copilot/FakeUpstreamServer.java b/java/sdk/src/test/java/com/github/copilot/FakeUpstreamServer.java similarity index 97% rename from java/src/test/java/com/github/copilot/FakeUpstreamServer.java rename to java/sdk/src/test/java/com/github/copilot/FakeUpstreamServer.java index 7af60d4d3..909cd1406 100644 --- a/java/src/test/java/com/github/copilot/FakeUpstreamServer.java +++ b/java/sdk/src/test/java/com/github/copilot/FakeUpstreamServer.java @@ -162,7 +162,10 @@ private void serveHttp(InputStream in, OutputStream out, String path, Map headers) throws Exception { String key = headers.get("sec-websocket-key"); - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + // SHA-1 is mandated by the WebSocket protocol (RFC 6455 §4.2.2) for the + // Sec-WebSocket-Accept handshake hash. This is NOT used for security purposes. + @SuppressWarnings("codeql[java/weak-cryptographic-algorithm]") + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); // lgtm[java/weak-cryptographic-algorithm] byte[] digest = sha1.digest((key + WS_MAGIC).getBytes(StandardCharsets.US_ASCII)); String accept = Base64.getEncoder().encodeToString(digest); String response = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" diff --git a/java/src/test/java/com/github/copilot/ForwardCompatibilityTest.java b/java/sdk/src/test/java/com/github/copilot/ForwardCompatibilityTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ForwardCompatibilityTest.java rename to java/sdk/src/test/java/com/github/copilot/ForwardCompatibilityTest.java diff --git a/java/src/test/java/com/github/copilot/GitHubTelemetryForwardingIT.java b/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryForwardingIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/GitHubTelemetryForwardingIT.java rename to java/sdk/src/test/java/com/github/copilot/GitHubTelemetryForwardingIT.java diff --git a/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java b/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java similarity index 91% rename from java/src/test/java/com/github/copilot/GitHubTelemetryTest.java rename to java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java index 8e35bd9a9..7b0deb997 100644 --- a/java/src/test/java/com/github/copilot/GitHubTelemetryTest.java +++ b/java/sdk/src/test/java/com/github/copilot/GitHubTelemetryTest.java @@ -259,23 +259,24 @@ void sendTelemetry(Object params) throws Exception { private void acceptLoop() { try { Socket socket = serverSocket.accept(); - JsonRpcClient server = JsonRpcClient.fromSocket(socket); - server.registerMethodHandler("connect", (id, params) -> { - connectParams.complete(params); - respond(server, id, Map.of("protocolVersion", 2)); + JsonRpcClient server = JsonRpcClient.fromSocket(socket, rpc -> { + rpc.registerMethodHandler("connect", (id, params) -> { + connectParams.complete(params); + respond(rpc, id, Map.of("protocolVersion", 2)); + }); + rpc.registerMethodHandler("session.create", (id, params) -> { + createParams.complete(params); + respond(rpc, id, Map.of("sessionId", params.path("sessionId").asText("created"), + "workspacePath", "/workspace")); + }); + rpc.registerMethodHandler("session.resume", (id, params) -> { + resumeParams.complete(params); + respond(rpc, id, Map.of("sessionId", params.path("sessionId").asText("resume-1"), + "workspacePath", "/workspace")); + }); + rpc.registerMethodHandler("session.destroy", (id, params) -> respond(rpc, id, Map.of())); + rpc.registerMethodHandler("runtime.shutdown", (id, params) -> respond(rpc, id, Map.of())); }); - server.registerMethodHandler("session.create", (id, params) -> { - createParams.complete(params); - respond(server, id, Map.of("sessionId", params.path("sessionId").asText("created"), "workspacePath", - "/workspace")); - }); - server.registerMethodHandler("session.resume", (id, params) -> { - resumeParams.complete(params); - respond(server, id, Map.of("sessionId", params.path("sessionId").asText("resume-1"), - "workspacePath", "/workspace")); - }); - server.registerMethodHandler("session.destroy", (id, params) -> respond(server, id, Map.of())); - server.registerMethodHandler("runtime.shutdown", (id, params) -> respond(server, id, Map.of())); ready.complete(server); } catch (IOException e) { ready.completeExceptionally(e); diff --git a/java/src/test/java/com/github/copilot/HooksTest.java b/java/sdk/src/test/java/com/github/copilot/HooksTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/HooksTest.java rename to java/sdk/src/test/java/com/github/copilot/HooksTest.java diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java b/java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/InternalExecutorProviderIT.java rename to java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderIT.java diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java b/java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java similarity index 100% rename from java/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java rename to java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderProbe.java diff --git a/java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java b/java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/InternalExecutorProviderTest.java rename to java/sdk/src/test/java/com/github/copilot/InternalExecutorProviderTest.java diff --git a/java/src/test/java/com/github/copilot/JsonIncludeNonNullTest.java b/java/sdk/src/test/java/com/github/copilot/JsonIncludeNonNullTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/JsonIncludeNonNullTest.java rename to java/sdk/src/test/java/com/github/copilot/JsonIncludeNonNullTest.java diff --git a/java/src/test/java/com/github/copilot/JsonRpcClientTest.java b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java similarity index 99% rename from java/src/test/java/com/github/copilot/JsonRpcClientTest.java rename to java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java index 3491ac8ab..d6c0b5e14 100644 --- a/java/src/test/java/com/github/copilot/JsonRpcClientTest.java +++ b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java @@ -135,7 +135,9 @@ void testIsConnectedWithSocketClosed() throws Exception { private static Process startBlockingProcess() throws IOException { boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); - return (isWindows ? new ProcessBuilder("cmd", "/c", "more") : new ProcessBuilder("cat")).start(); + return (isWindows + ? new ProcessBuilder(System.getenv("COMSPEC"), "/c", "more") + : new ProcessBuilder("/usr/bin/cat")).start(); } @Test diff --git a/java/src/test/java/com/github/copilot/LifecycleEventManagerTest.java b/java/sdk/src/test/java/com/github/copilot/LifecycleEventManagerTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/LifecycleEventManagerTest.java rename to java/sdk/src/test/java/com/github/copilot/LifecycleEventManagerTest.java diff --git a/java/src/test/java/com/github/copilot/LowLevelToolDefinitionIT.java b/java/sdk/src/test/java/com/github/copilot/LowLevelToolDefinitionIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/LowLevelToolDefinitionIT.java rename to java/sdk/src/test/java/com/github/copilot/LowLevelToolDefinitionIT.java diff --git a/java/src/test/java/com/github/copilot/ManagedSettingsTest.java b/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ManagedSettingsTest.java rename to java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java diff --git a/java/src/test/java/com/github/copilot/McpAndAgentsTest.java b/java/sdk/src/test/java/com/github/copilot/McpAndAgentsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/McpAndAgentsTest.java rename to java/sdk/src/test/java/com/github/copilot/McpAndAgentsTest.java diff --git a/java/src/test/java/com/github/copilot/McpAuthInterestRegistrationTest.java b/java/sdk/src/test/java/com/github/copilot/McpAuthInterestRegistrationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/McpAuthInterestRegistrationTest.java rename to java/sdk/src/test/java/com/github/copilot/McpAuthInterestRegistrationTest.java diff --git a/java/src/test/java/com/github/copilot/McpOAuthE2ETest.java b/java/sdk/src/test/java/com/github/copilot/McpOAuthE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/McpOAuthE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/McpOAuthE2ETest.java diff --git a/java/src/test/java/com/github/copilot/McpOAuthResumeE2ETest.java b/java/sdk/src/test/java/com/github/copilot/McpOAuthResumeE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/McpOAuthResumeE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/McpOAuthResumeE2ETest.java diff --git a/java/src/test/java/com/github/copilot/MessageAttachmentTest.java b/java/sdk/src/test/java/com/github/copilot/MessageAttachmentTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/MessageAttachmentTest.java rename to java/sdk/src/test/java/com/github/copilot/MessageAttachmentTest.java diff --git a/java/src/test/java/com/github/copilot/MetadataApiTest.java b/java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/MetadataApiTest.java rename to java/sdk/src/test/java/com/github/copilot/MetadataApiTest.java diff --git a/java/src/test/java/com/github/copilot/ModeHandlersTest.java b/java/sdk/src/test/java/com/github/copilot/ModeHandlersTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ModeHandlersTest.java rename to java/sdk/src/test/java/com/github/copilot/ModeHandlersTest.java diff --git a/java/src/test/java/com/github/copilot/ModelInfoTest.java b/java/sdk/src/test/java/com/github/copilot/ModelInfoTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ModelInfoTest.java rename to java/sdk/src/test/java/com/github/copilot/ModelInfoTest.java diff --git a/java/src/test/java/com/github/copilot/ModuleDescriptorTest.java b/java/sdk/src/test/java/com/github/copilot/ModuleDescriptorTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ModuleDescriptorTest.java rename to java/sdk/src/test/java/com/github/copilot/ModuleDescriptorTest.java diff --git a/java/src/test/java/com/github/copilot/MultiProviderConfigTest.java b/java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/MultiProviderConfigTest.java rename to java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java diff --git a/java/src/test/java/com/github/copilot/MultiProviderRegistryE2ETest.java b/java/sdk/src/test/java/com/github/copilot/MultiProviderRegistryE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/MultiProviderRegistryE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/MultiProviderRegistryE2ETest.java diff --git a/java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java b/java/sdk/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java rename to java/sdk/src/test/java/com/github/copilot/OptionalApiAndJacksonTest.java diff --git a/java/src/test/java/com/github/copilot/PerSessionAuthTest.java b/java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/PerSessionAuthTest.java rename to java/sdk/src/test/java/com/github/copilot/PerSessionAuthTest.java diff --git a/java/src/test/java/com/github/copilot/PermissionRequestResultKindTest.java b/java/sdk/src/test/java/com/github/copilot/PermissionRequestResultKindTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/PermissionRequestResultKindTest.java rename to java/sdk/src/test/java/com/github/copilot/PermissionRequestResultKindTest.java diff --git a/java/src/test/java/com/github/copilot/PermissionRequestResultTest.java b/java/sdk/src/test/java/com/github/copilot/PermissionRequestResultTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/PermissionRequestResultTest.java rename to java/sdk/src/test/java/com/github/copilot/PermissionRequestResultTest.java diff --git a/java/src/test/java/com/github/copilot/PermissionsTest.java b/java/sdk/src/test/java/com/github/copilot/PermissionsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/PermissionsTest.java rename to java/sdk/src/test/java/com/github/copilot/PermissionsTest.java diff --git a/java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java b/java/sdk/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java rename to java/sdk/src/test/java/com/github/copilot/PreMcpToolCallHookTest.java diff --git a/java/src/test/java/com/github/copilot/ProviderConfigTest.java b/java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ProviderConfigTest.java rename to java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java diff --git a/java/src/test/java/com/github/copilot/ProviderEndpointE2ETest.java b/java/sdk/src/test/java/com/github/copilot/ProviderEndpointE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ProviderEndpointE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/ProviderEndpointE2ETest.java diff --git a/java/src/test/java/com/github/copilot/RemoteSessionTest.java b/java/sdk/src/test/java/com/github/copilot/RemoteSessionTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RemoteSessionTest.java rename to java/sdk/src/test/java/com/github/copilot/RemoteSessionTest.java diff --git a/java/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java b/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java rename to java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java diff --git a/java/src/test/java/com/github/copilot/RpcServerE2ETest.java b/java/sdk/src/test/java/com/github/copilot/RpcServerE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcServerE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/RpcServerE2ETest.java diff --git a/java/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java b/java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/RpcServerMiscE2ETest.java diff --git a/java/src/test/java/com/github/copilot/RpcSessionStateExtrasE2ETest.java b/java/sdk/src/test/java/com/github/copilot/RpcSessionStateExtrasE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcSessionStateExtrasE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/RpcSessionStateExtrasE2ETest.java diff --git a/java/src/test/java/com/github/copilot/RpcTasksAndHandlersE2ETest.java b/java/sdk/src/test/java/com/github/copilot/RpcTasksAndHandlersE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcTasksAndHandlersE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/RpcTasksAndHandlersE2ETest.java diff --git a/java/src/test/java/com/github/copilot/RpcWrappersTest.java b/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/RpcWrappersTest.java rename to java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java diff --git a/java/src/test/java/com/github/copilot/SchedulerShutdownRaceTest.java b/java/sdk/src/test/java/com/github/copilot/SchedulerShutdownRaceTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SchedulerShutdownRaceTest.java rename to java/sdk/src/test/java/com/github/copilot/SchedulerShutdownRaceTest.java diff --git a/java/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java b/java/sdk/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionCanvasSnapshotTest.java diff --git a/java/src/test/java/com/github/copilot/SessionConfigE2ETest.java b/java/sdk/src/test/java/com/github/copilot/SessionConfigE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionConfigE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/SessionConfigE2ETest.java diff --git a/java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java b/java/sdk/src/test/java/com/github/copilot/SessionEventDeserializationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionEventDeserializationTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionEventDeserializationTest.java diff --git a/java/src/test/java/com/github/copilot/SessionEventHandlingTest.java b/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionEventHandlingTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java diff --git a/java/src/test/java/com/github/copilot/SessionEventsE2ETest.java b/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java similarity index 98% rename from java/src/test/java/com/github/copilot/SessionEventsE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java index 161839a53..dad75db52 100644 --- a/java/src/test/java/com/github/copilot/SessionEventsE2ETest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java @@ -5,6 +5,7 @@ package com.github.copilot; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Files; @@ -184,6 +185,9 @@ void testShouldReceiveSessionEvents_assistantUsageEvent() throws Exception { // Usage events may or may not be emitted depending on the model/API version // This test verifies the event handler works when they are emitted // We don't assert they must be present since it depends on the backend + if (!usageEvents.isEmpty()) { + assertNotNull(usageEvents.get(0).getData(), "Usage event should carry data"); + } } } diff --git a/java/src/test/java/com/github/copilot/SessionHandlerTest.java b/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionHandlerTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java diff --git a/java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java b/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionRequestBuilderTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java diff --git a/java/src/test/java/com/github/copilot/SessionTodosChangedTest.java b/java/sdk/src/test/java/com/github/copilot/SessionTodosChangedTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SessionTodosChangedTest.java rename to java/sdk/src/test/java/com/github/copilot/SessionTodosChangedTest.java diff --git a/java/src/test/java/com/github/copilot/SkillsTest.java b/java/sdk/src/test/java/com/github/copilot/SkillsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SkillsTest.java rename to java/sdk/src/test/java/com/github/copilot/SkillsTest.java diff --git a/java/src/test/java/com/github/copilot/SlashCommandsIT.java b/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java similarity index 98% rename from java/src/test/java/com/github/copilot/SlashCommandsIT.java rename to java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java index 634c0bad9..5dec06464 100644 --- a/java/src/test/java/com/github/copilot/SlashCommandsIT.java +++ b/java/sdk/src/test/java/com/github/copilot/SlashCommandsIT.java @@ -22,6 +22,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import com.github.copilot.e2e.SkipInProcess; + import com.github.copilot.generated.rpc.SessionCommandsListResult; import com.github.copilot.generated.rpc.SessionCommandsInvokeParams; import com.github.copilot.generated.rpc.SlashCommandAgentPromptResult; @@ -41,6 +43,7 @@ * Requires the CLI to be installed and the user to be signed in. Uses * {@link TestUtil#findCliPath()} so the test harness binary is found in CI. */ +@SkipInProcess("Requires a live signed-in CLI subprocess and logged-in-user transport behavior rather than the replayed in-process harness") class SlashCommandsIT { private static CopilotClient client; diff --git a/java/src/test/java/com/github/copilot/StreamingFidelityTest.java b/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java similarity index 86% rename from java/src/test/java/com/github/copilot/StreamingFidelityTest.java rename to java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java index bc4999a16..3701cf9c1 100644 --- a/java/src/test/java/com/github/copilot/StreamingFidelityTest.java +++ b/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java @@ -249,33 +249,37 @@ void testShouldNotProduceDeltasAfterSessionResumeWithStreamingDisabled() throws */ @Test void testShouldEmitStreamingDeltasWithReasoningEffortConfigured() throws Exception { - ctx.configureForTest("streaming_fidelity", "should_emit_streaming_deltas_with_reasoning_effort_configured"); + try (E2ETestContext isolatedContext = E2ETestContext.create()) { + isolatedContext.configureForTest("streaming_fidelity", + "should_emit_streaming_deltas_with_reasoning_effort_configured"); - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client - .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) - .setModel("gpt-5.4").setStreaming(true).setReasoningEffort("high")) - .get(); + try (CopilotClient client = isolatedContext.createClient()) { + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setModel("gpt-5.4").setStreaming(true).setReasoningEffort("high")) + .get(); - List events = new ArrayList<>(); - session.on(events::add); + List events = new ArrayList<>(); + session.on(events::add); - session.sendAndWait(new MessageOptions().setPrompt("What is 15 * 17?")).get(60, TimeUnit.SECONDS); + session.sendAndWait(new MessageOptions().setPrompt("What is 15 * 17?")).get(60, TimeUnit.SECONDS); - // With streaming + reasoning effort, we should still get content deltas - List deltaEvents = events.stream() - .filter(e -> e instanceof AssistantMessageDeltaEvent).map(e -> (AssistantMessageDeltaEvent) e) - .toList(); - assertFalse(deltaEvents.isEmpty(), "Should have received delta events with reasoning effort configured"); + // With streaming + reasoning effort, we should still get content deltas + List deltaEvents = events.stream() + .filter(e -> e instanceof AssistantMessageDeltaEvent).map(e -> (AssistantMessageDeltaEvent) e) + .toList(); + assertFalse(deltaEvents.isEmpty(), + "Should have received delta events with reasoning effort configured"); - // And a final assistant.message with the answer - List assistantEvents = events.stream() - .filter(e -> e instanceof AssistantMessageEvent).map(e -> (AssistantMessageEvent) e).toList(); - assertFalse(assistantEvents.isEmpty(), "Should have received assistant message events"); - assertTrue(assistantEvents.get(assistantEvents.size() - 1).getData().content().contains("255"), - "Response should contain 255"); + // And a final assistant.message with the answer + List assistantEvents = events.stream() + .filter(e -> e instanceof AssistantMessageEvent).map(e -> (AssistantMessageEvent) e).toList(); + assertFalse(assistantEvents.isEmpty(), "Should have received assistant message events"); + assertTrue(assistantEvents.get(assistantEvents.size() - 1).getData().content().contains("255"), + "Response should contain 255"); - session.close(); + session.close(); + } } } } diff --git a/java/src/test/java/com/github/copilot/SubagentHooksE2ETest.java b/java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java similarity index 100% rename from java/src/test/java/com/github/copilot/SubagentHooksE2ETest.java rename to java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java diff --git a/java/src/test/java/com/github/copilot/SystemMessageSectionsIT.java b/java/sdk/src/test/java/com/github/copilot/SystemMessageSectionsIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/SystemMessageSectionsIT.java rename to java/sdk/src/test/java/com/github/copilot/SystemMessageSectionsIT.java diff --git a/java/src/test/java/com/github/copilot/TelemetryConfigTest.java b/java/sdk/src/test/java/com/github/copilot/TelemetryConfigTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/TelemetryConfigTest.java rename to java/sdk/src/test/java/com/github/copilot/TelemetryConfigTest.java diff --git a/java/src/test/java/com/github/copilot/TestUtil.java b/java/sdk/src/test/java/com/github/copilot/TestUtil.java similarity index 100% rename from java/src/test/java/com/github/copilot/TestUtil.java rename to java/sdk/src/test/java/com/github/copilot/TestUtil.java diff --git a/java/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java b/java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java rename to java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java diff --git a/java/src/test/java/com/github/copilot/ToolDefinitionTest.java b/java/sdk/src/test/java/com/github/copilot/ToolDefinitionTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolDefinitionTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolDefinitionTest.java diff --git a/java/src/test/java/com/github/copilot/ToolInvocationTest.java b/java/sdk/src/test/java/com/github/copilot/ToolInvocationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolInvocationTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolInvocationTest.java diff --git a/java/src/test/java/com/github/copilot/ToolResultObjectSerializationTest.java b/java/sdk/src/test/java/com/github/copilot/ToolResultObjectSerializationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolResultObjectSerializationTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolResultObjectSerializationTest.java diff --git a/java/src/test/java/com/github/copilot/ToolResultsTest.java b/java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolResultsTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java diff --git a/java/src/test/java/com/github/copilot/ToolSetTest.java b/java/sdk/src/test/java/com/github/copilot/ToolSetTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolSetTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolSetTest.java diff --git a/java/src/test/java/com/github/copilot/ToolsTest.java b/java/sdk/src/test/java/com/github/copilot/ToolsTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ToolsTest.java rename to java/sdk/src/test/java/com/github/copilot/ToolsTest.java diff --git a/java/src/test/java/com/github/copilot/UpdateSessionOptionsForModeTest.java b/java/sdk/src/test/java/com/github/copilot/UpdateSessionOptionsForModeTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/UpdateSessionOptionsForModeTest.java rename to java/sdk/src/test/java/com/github/copilot/UpdateSessionOptionsForModeTest.java diff --git a/java/src/test/java/com/github/copilot/ZeroTimeoutContractTest.java b/java/sdk/src/test/java/com/github/copilot/ZeroTimeoutContractTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/ZeroTimeoutContractTest.java rename to java/sdk/src/test/java/com/github/copilot/ZeroTimeoutContractTest.java diff --git a/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicTestTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java b/java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java rename to java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicTestTools.java diff --git a/java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java b/java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java rename to java/sdk/src/test/java/com/github/copilot/e2e/ErgonomicToolDefinitionIT.java diff --git a/java/sdk/src/test/java/com/github/copilot/e2e/InProcessTransportIT.java b/java/sdk/src/test/java/com/github/copilot/e2e/InProcessTransportIT.java new file mode 100644 index 000000000..1b8595401 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/e2e/InProcessTransportIT.java @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.e2e; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.Map; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.github.copilot.AllowCopilotExperimental; +import com.github.copilot.CopilotClient; +import com.github.copilot.E2ETestContext; +import com.github.copilot.ffi.InProcessEnvGuard; +import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.PingResponse; +import com.github.copilot.rpc.RuntimeConnection; + +/** + * Failsafe integration test for the in-process (FFI) transport. + * + *

+ * Loads the real {@code runtime.node} native library into this test process via + * {@link com.github.copilot.ffi.FfiRuntimeHost}, performs a purely local + * {@code ping} round-trip through the runtime, and stops cleanly. {@code ping} + * is answered by the runtime itself, so no auth or replay proxy is involved — + * this mirrors {@code nodejs/test/e2e/inprocess_ffi.e2e.test.ts}, + * {@code go/internal/e2e/inprocess_ffi_e2e_test.go}, and + * {@code python/e2e/test_inprocess_ffi_e2e.py}. + * + *

+ * {@link InProcessEnvGuard} demonstrates how the harness redirects the native + * runtime's HTTP traffic to the replay proxy (via {@code COPILOT_API_URL}) for + * tests that need session/message round trips over the in-process transport: + * the native library reads environment variables from the live OS process + * environment block, not from the JVM's {@code System.getenv()} snapshot, so + * only a JNA-backed native call can make it visible to code already loaded + * in-process. + * + *

+ * Run with {@code mvn verify -Pinprocess} from the {@code java} reactor root, + * which builds the {@code copilot-sdk-java-runtime} artifact and sets + * {@code COPILOT_CLI_PATH} to the pinned CLI whose sibling {@code runtime.node} + * this test loads, and forces {@code forkCount=1} because the FFI host and env + * guard mutate process-global state. + * + *

+ * {@link RequireInProcess} disables this test unless the {@code -Pinprocess} + * profile is active: without it, the {@code copilot-sdk-java-runtime} + * classifier JAR providing {@code runtime.node} is not on the classpath, so the + * test would fail with a {@code FileNotFoundException} rather than being + * skipped. + */ +@AllowCopilotExperimental +@RequireInProcess +class InProcessTransportIT { + + private static E2ETestContext ctx; + + @BeforeAll + static void setup() throws Exception { + ctx = E2ETestContext.create(); + } + + @AfterAll + static void teardown() throws Exception { + if (ctx != null) { + ctx.close(); + } + } + + @Test + void shouldStartPingAndStopOverInProcessFfi() throws Exception { + // Route the native runtime's HTTP traffic (should it make any) at the + // replay proxy, mirroring how a session-level in-process test would + // redirect COPILOT_API_URL. `ping` never reaches the network, but this + // demonstrates the guard's intended usage for future in-process tests. + // COPILOT_CLI_PATH is intentionally NOT set here: NativeRuntimeLoader and + // CopilotClient.resolveInProcessEntrypoint() read it via + // System.getenv(), which is a JVM-startup-time snapshot that native + // setenv() calls made after the JVM starts cannot update — it must be + // set before the JVM starts (see the -Pinprocess Maven profile). + try (InProcessEnvGuard envGuard = new InProcessEnvGuard(Map.of("COPILOT_API_URL", ctx.getProxyUrl()))) { + CopilotClientOptions options = new CopilotClientOptions().setConnection(RuntimeConnection.forInProcess()); + try (CopilotClient client = new CopilotClient(options)) { + client.start().get(); + + PingResponse pong = client.ping("ffi message").get(); + assertEquals("pong: ffi message", pong.message()); + assertNotNull(pong.timestamp()); + + client.stop().get(); + } + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java b/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java new file mode 100644 index 000000000..12de4e5b7 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/e2e/RequireInProcess.java @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.e2e; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Enables an annotated test class or method only when the E2E suite runs under + * the in-process (FFI) transport, i.e. when + * {@code COPILOT_SDK_DEFAULT_CONNECTION} is set to {@code inprocess}. + * + *

+ * Use this for tests that require the real {@code runtime.node} native library + * to be present on the classpath, which only the {@code -Pinprocess} Maven + * profile guarantees (see {@link InProcessTransportIT}). Without this profile, + * standard {@code mvn verify} runs would fail with a + * {@code FileNotFoundException} because the classifier JAR providing + * {@code runtime.node} is not on the classpath. + *

+ * + *

+ * The inverse of {@link SkipInProcess}. + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@ExtendWith(RequireInProcess.Condition.class) +public @interface RequireInProcess { + + /** + * Explains why the annotated test requires the in-process transport. + * + * @return the skip reason used when the in-process transport is not active + */ + String value() default "Requires the -Pinprocess Maven profile"; + + /** + * JUnit 5 execution condition backing {@link RequireInProcess}. + */ + public static final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition { + + private static final String DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION"; + + @Override + public org.junit.jupiter.api.extension.ConditionEvaluationResult evaluateExecutionCondition( + org.junit.jupiter.api.extension.ExtensionContext context) { + String envValue = System.getenv(DEFAULT_CONNECTION_ENV_VAR); + if ("inprocess".equalsIgnoreCase(envValue)) { + return org.junit.jupiter.api.extension.ConditionEvaluationResult + .enabled("Running under the in-process transport"); + } + String reason = context.getElement().map(element -> element.getAnnotation(RequireInProcess.class)) + .map(RequireInProcess::value).orElse("Requires the -Pinprocess Maven profile"); + return org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled(reason); + } + } +} diff --git a/java/src/test/java/com/github/copilot/e2e/RewindIT.java b/java/sdk/src/test/java/com/github/copilot/e2e/RewindIT.java similarity index 100% rename from java/src/test/java/com/github/copilot/e2e/RewindIT.java rename to java/sdk/src/test/java/com/github/copilot/e2e/RewindIT.java diff --git a/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java b/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java new file mode 100644 index 000000000..3f626e133 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/e2e/SkipInProcess.java @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.e2e; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Disables an annotated test class or method when the E2E suite runs under the + * in-process (FFI) transport, i.e. when {@code COPILOT_SDK_DEFAULT_CONNECTION} + * is set to {@code inprocess}. + * + *

+ * Use this for tests that rely on per-client process settings the in-process + * transport cannot honor — for example per-client environment variables, since + * the in-process runtime shares the host process's single environment (see + * {@link com.github.copilot.rpc.InProcessRuntimeConnection} and + * issue #1934). + *

+ * + *

+ * Mirrors {@code skip_inprocess(reason)} in the Rust E2E harness. + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@ExtendWith(SkipInProcess.Condition.class) +public @interface SkipInProcess { + + /** + * Explains why the annotated test is incompatible with the in-process + * transport. + * + * @return the skip reason + */ + String value() default "Not supported under the in-process (FFI) transport"; + + /** + * JUnit 5 execution condition backing {@link SkipInProcess}. + */ + public static final class Condition implements org.junit.jupiter.api.extension.ExecutionCondition { + + private static final String DEFAULT_CONNECTION_ENV_VAR = "COPILOT_SDK_DEFAULT_CONNECTION"; + + @Override + public org.junit.jupiter.api.extension.ConditionEvaluationResult evaluateExecutionCondition( + org.junit.jupiter.api.extension.ExtensionContext context) { + String envValue = System.getenv(DEFAULT_CONNECTION_ENV_VAR); + if (!"inprocess".equalsIgnoreCase(envValue)) { + return org.junit.jupiter.api.extension.ConditionEvaluationResult + .enabled("Not running under the in-process transport"); + } + String reason = context.getElement().map(element -> element.getAnnotation(SkipInProcess.class)) + .map(SkipInProcess::value).orElse("Not supported under the in-process (FFI) transport"); + return org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled(reason); + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java new file mode 100644 index 000000000..cc98d24f6 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/FfiRuntimeHostTest.java @@ -0,0 +1,362 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.rpc.CopilotClientMode; +import com.github.copilot.rpc.CopilotClientOptions; +import com.sun.jna.Memory; +import com.sun.jna.Pointer; + +class FfiRuntimeHostTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + void startBuildsExpectedArgvAndEnvJson() throws Exception { + class RecordingBinding implements NativeBinding { + byte[] argv; + byte[] env; + + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + this.argv = argvJson; + this.env = envJson; + return 11; + } + + @Override + public boolean hostShutdown(int serverId) { + return true; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + return 21; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + return true; + } + } + + RecordingBinding binding = new RecordingBinding(); + CopilotClientOptions options = new CopilotClientOptions().setLogLevel("debug").setGitHubToken("gh-token") + .setCopilotHome("/tmp/copilot-home").setUseLoggedInUser(false).setSessionIdleTimeoutSeconds(42) + .setRemote(true).setMode(CopilotClientMode.EMPTY).setCliArgs(new String[]{"--extra-flag"}); + + FfiRuntimeHost host = new FfiRuntimeHost(binding, "/tmp/runtime.node"); + host.start("/tmp/entrypoint.js", options); + + List argv = MAPPER.readValue(binding.argv, new TypeReference>() { + }); + assertEquals("node", argv.get(0)); + assertEquals("/tmp/entrypoint.js", argv.get(1)); + assertTrue(argv.contains("--embedded-host")); + assertTrue(argv.contains("--no-auto-update")); + assertTrue(argv.contains("--auth-token-env")); + assertTrue(argv.contains("COPILOT_SDK_AUTH_TOKEN")); + assertTrue(argv.contains("--no-auto-login")); + assertTrue(argv.contains("--session-idle-timeout")); + assertTrue(argv.contains("42")); + assertTrue(argv.contains("--remote")); + assertTrue(argv.contains("--extra-flag")); + + Map env = MAPPER.readValue(binding.env, new TypeReference>() { + }); + assertEquals("gh-token", env.get("COPILOT_SDK_AUTH_TOKEN")); + assertEquals("/tmp/copilot-home", env.get("COPILOT_HOME")); + assertEquals("1", env.get("COPILOT_DISABLE_KEYTAR")); + } + + @Test + void callbackExceptionIsContainedAndDoesNotEscapeAcrossFfiBoundary() { + AtomicBoolean callbackReturned = new AtomicBoolean(false); + NativeBinding binding = new NativeBinding() { + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return 1; + } + + @Override + public boolean hostShutdown(int serverId) { + return true; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + Memory mem = new Memory(5); + mem.write(0, "hello".getBytes(StandardCharsets.UTF_8), 0, 5); + callback.invoke(Pointer.NULL, mem, new SizeT(5)); + callbackReturned.set(true); + return 2; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + return true; + } + }; + + QueueInputStream throwingStream = new QueueInputStream() { + @Override + void enqueue(byte[] bytes) { + throw new RuntimeException("boom"); + } + }; + + FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib", throwingStream); + assertDoesNotThrow(() -> host.start("/tmp/entrypoint", new CopilotClientOptions())); + assertTrue(callbackReturned.get(), "callback should return normally even when enqueue throws"); + } + + @Test + void closeNeverThrowsEvenWhenNativeCloseFails() { + NativeBinding binding = new NativeBinding() { + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return 5; + } + + @Override + public boolean hostShutdown(int serverId) { + throw new RuntimeException("shutdown failed"); + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + return 9; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + throw new RuntimeException("close failed"); + } + }; + + FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib"); + host.start("/tmp/entrypoint", new CopilotClientOptions()); + assertDoesNotThrow(host::close); + } + + @Test + void failedConnectionOpenReleasesHostForSequentialStartup() { + AtomicInteger starts = new AtomicInteger(); + AtomicInteger shutdowns = new AtomicInteger(); + NativeBinding binding = new NativeBinding() { + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return starts.incrementAndGet(); + } + + @Override + public boolean hostShutdown(int serverId) { + shutdowns.incrementAndGet(); + return true; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + return serverId == 1 ? 0 : 22; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + return true; + } + }; + + try (FfiRuntimeHost failedHost = new FfiRuntimeHost(binding, "test-lib")) { + assertThrows(IllegalStateException.class, + () -> failedHost.start("/tmp/entrypoint", new CopilotClientOptions())); + } + assertEquals(1, shutdowns.get(), "failed connection startup must release its native host"); + + try (FfiRuntimeHost nextHost = new FfiRuntimeHost(binding, "test-lib")) { + assertDoesNotThrow(() -> nextHost.start("/tmp/entrypoint", new CopilotClientOptions())); + } + assertEquals(2, shutdowns.get(), "the sequential host must also shut down cleanly"); + } + + @Test + void writeAndCloseAreSerializedByOperationLock() throws Exception { + CountDownLatch writeStarted = new CountDownLatch(1); + CountDownLatch allowWriteToFinish = new CountDownLatch(1); + AtomicInteger writes = new AtomicInteger(0); + + NativeBinding binding = new NativeBinding() { + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return 3; + } + + @Override + public boolean hostShutdown(int serverId) { + return true; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + return 4; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + writes.incrementAndGet(); + writeStarted.countDown(); + try { + allowWriteToFinish.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + return true; + } + }; + + FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib"); + host.start("/tmp/entrypoint", new CopilotClientOptions()); + + CompletableFuture writer = CompletableFuture.runAsync(() -> { + try { + host.getSendStream().write("ping".getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + assertTrue(writeStarted.await(2, TimeUnit.SECONDS)); + CompletableFuture closer = CompletableFuture.runAsync(host::close); + allowWriteToFinish.countDown(); + + writer.get(5, TimeUnit.SECONDS); + closer.get(5, TimeUnit.SECONDS); + assertEquals(1, writes.get()); + assertThrows(IOException.class, () -> host.getSendStream().write("late".getBytes(StandardCharsets.UTF_8))); + } + + @Test + void closeDrainsActiveCallbacksBeforeHostShutdown() throws Exception { + CountDownLatch callbackEntered = new CountDownLatch(1); + CountDownLatch allowCallbackToReturn = new CountDownLatch(1); + AtomicBoolean shutdownObservedAfterCallbackReturn = new AtomicBoolean(false); + AtomicBoolean callbackFinished = new AtomicBoolean(false); + AtomicReference callbackRef = new AtomicReference<>(); + + NativeBinding binding = new NativeBinding() { + @Override + public int hostStart(byte[] argvJson, int argvJsonLen, byte[] envJson, int envJsonLen) { + return 7; + } + + @Override + public boolean hostShutdown(int serverId) { + shutdownObservedAfterCallbackReturn.set(callbackFinished.get()); + return true; + } + + @Override + public int connectionOpen(int serverId, OutboundCallback callback, Pointer userData, byte[] extSource, + int extSourceLen, byte[] extName, int extNameLen, byte[] connToken, int connTokenLen) { + callbackRef.set(callback); + return 8; + } + + @Override + public boolean connectionWrite(int connectionId, byte[] data, int dataLen) { + return true; + } + + @Override + public boolean connectionClose(int connectionId) { + return true; + } + }; + + QueueInputStream blockingStream = new QueueInputStream() { + @Override + void enqueue(byte[] bytes) { + callbackEntered.countDown(); + try { + allowCallbackToReturn.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + callbackFinished.set(true); + super.enqueue(bytes); + } + }; + + FfiRuntimeHost host = new FfiRuntimeHost(binding, "test-lib", blockingStream); + host.start("/tmp/entrypoint", new CopilotClientOptions()); + assertNotNull(callbackRef.get()); + + CompletableFuture callbackFuture = CompletableFuture.runAsync(() -> { + Memory mem = new Memory(1); + mem.setByte(0, (byte) 'x'); + callbackRef.get().invoke(Pointer.NULL, mem, new SizeT(1)); + }); + + assertTrue(callbackEntered.await(2, TimeUnit.SECONDS)); + CompletableFuture closeFuture = CompletableFuture.runAsync(host::close); + Thread.sleep(150); + assertFalse(closeFuture.isDone(), "close should wait for active callback to drain"); + allowCallbackToReturn.countDown(); + callbackFuture.get(5, TimeUnit.SECONDS); + closeFuture.get(5, TimeUnit.SECONDS); + assertTrue(shutdownObservedAfterCallbackReturn.get(), "host_shutdown should run after callback drains"); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java b/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java new file mode 100644 index 000000000..43df71371 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/InProcessEnvGuard.java @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Logger; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.WString; + +/** + * Mutates the live process environment block so that native code loaded + * in-process (e.g. {@code runtime.node} via JNA) observes the given environment + * variables, and restores the previous values on {@link #close()}. + * + *

+ * Java has no public API to modify the process-level environment block: + * {@code System.setProperty()} only writes the JVM property bag, and + * {@code System.getenv()} is an immutable startup-time snapshot. Native code + * loaded via JNA reads the OS environment directly + * ({@code GetEnvironmentVariableW} on Windows, {@code getenv()} on POSIX), so + * the only way to make it see an overridden value is to call the OS API + * directly through JNA. + *

+ * + *

+ * This mirrors the Rust {@code InProcessEnvGuard} + * ({@code rust/tests/e2e/support.rs}) and the .NET + * {@code InProcessEnvIsolation} + * ({@code dotnet/test/Harness/InProcessEnvIsolation.cs}). + *

+ * + *

+ * Thread safety: this guard mutates process-global state. + * Tests that use it must run with test concurrency 1 (see the + * {@code -Pinprocess} Maven profile, which sets {@code failsafe.forkCount=1} + * and disables parallel execution). + *

+ */ +public final class InProcessEnvGuard implements AutoCloseable { + + private static final Logger LOG = Logger.getLogger(InProcessEnvGuard.class.getName()); + + /** + * Environment variables suppressed because replay snapshots expect Bearer/OAuth + * auth. + */ + private static final List SUPPRESSED_KEYS = List.of("COPILOT_HMAC_KEY", "CAPI_HMAC_KEY"); + + /** + * Windows kernel32: sets or deletes a variable in the process environment + * block. + */ + private interface Kernel32Env extends Library { + boolean SetEnvironmentVariableW(WString lpName, WString lpValue); + + int GetEnvironmentVariableW(WString lpName, char[] lpBuffer, int nSize); + } + + /** POSIX libc: sets or deletes a variable in the process environment block. */ + private interface LibcEnv extends Library { + int setenv(String name, String value, int overwrite); + + int unsetenv(String name); + + /** Returns null if the variable is not set. */ + String getenv(String name); + } + + /** + * Sentinel indicating the variable was not set (distinct from empty string). + */ + private static final String ABSENT_SENTINEL = new String("\0ABSENT\0"); + + /** + * name -> previous value ({@code null} means the variable was not set before). + */ + private final List> saved = new ArrayList<>(); + private boolean closed; + + /** + * Applies {@code applyEnv} to the native process environment block, saving the + * previous values for restoration by {@link #close()}. Also suppresses + * {@code COPILOT_HMAC_KEY} / {@code CAPI_HMAC_KEY} if present, since the replay + * proxy expects Bearer/OAuth auth rather than HMAC. + * + * @param applyEnv + * environment variables to apply; values must not be {@code null} + */ + public InProcessEnvGuard(Map applyEnv) { + for (Map.Entry entry : applyEnv.entrySet()) { + apply(entry.getKey(), entry.getValue()); + } + for (String key : SUPPRESSED_KEYS) { + String previous = nativeGetEnv(key); + if (previous != null && !previous.isEmpty()) { + apply(key, null); + } + } + } + + private void apply(String name, String value) { + String previous = nativeGetEnv(name); + saved.add(Map.entry(name, previous == null ? ABSENT_SENTINEL : previous)); + nativeSetEnv(name, value); + } + + /** + * Restores every environment variable this guard touched to the value it had + * before construction. + */ + @Override + public synchronized void close() { + if (closed) { + return; + } + closed = true; + List> reversed = new ArrayList<>(saved); + Collections.reverse(reversed); + for (Map.Entry entry : reversed) { + // ABSENT_SENTINEL uses a value ("\0ABSENT\0") impossible in real env vars. + String restoreValue = ABSENT_SENTINEL.equals(entry.getValue()) ? null : entry.getValue(); + nativeSetEnv(entry.getKey(), restoreValue); + } + } + + private static String nativeGetEnv(String name) { + if (isWindows()) { + return nativeGetEnvWindows(name); + } else { + return nativeGetEnvUnix(name); + } + } + + private static String nativeGetEnvWindows(String name) { + Kernel32Env kernel32 = Native.load("kernel32", Kernel32Env.class); + char[] buffer = new char[32767]; + int len = kernel32.GetEnvironmentVariableW(new WString(name), buffer, buffer.length); + if (len == 0) { + // Variable not set (or error — treat as absent) + return null; + } + return new String(buffer, 0, len); + } + + private static String nativeGetEnvUnix(String name) { + LibcEnv libc = Native.load("c", LibcEnv.class); + return libc.getenv(name); + } + + private static void nativeSetEnv(String name, String value) { + if (isWindows()) { + nativeSetEnvWindows(name, value); + } else { + nativeSetEnvUnix(name, value); + } + } + + private static void nativeSetEnvWindows(String name, String value) { + Kernel32Env kernel32 = Native.load("kernel32", Kernel32Env.class); + boolean ok = kernel32.SetEnvironmentVariableW(new WString(name), value != null ? new WString(value) : null); + if (!ok) { + LOG.warning("SetEnvironmentVariableW failed for key=" + name); + } + } + + private static void nativeSetEnvUnix(String name, String value) { + LibcEnv libc = Native.load("c", LibcEnv.class); + if (value != null) { + int rc = libc.setenv(name, value, 1); + if (rc != 0) { + LOG.warning("setenv() failed for key=" + name + " rc=" + rc); + } + } else { + int rc = libc.unsetenv(name); + if (rc != 0) { + LOG.warning("unsetenv() failed for key=" + name + " rc=" + rc); + } + } + } + + private static boolean isWindows() { + return System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("win"); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java new file mode 100644 index 000000000..d7d218c0e --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/JnaNativeBindingTest.java @@ -0,0 +1,344 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import com.sun.jna.Pointer; + +import java.lang.ref.WeakReference; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Unit tests for {@link JnaNativeBinding}. + * + *

+ * Delegation and callback-tracking tests use a stub + * {@link JnaNativeBinding.CopilotRuntimeLibrary}. Library-loading guard tests + * exercise {@link JnaNativeBinding} directly against the real + * {@code runtime.node} when it is available on the test classpath. + * + *

+ * Tests that require the packaged native runtime are conditionally skipped when + * it is unavailable (for example, when not running with {@code -Pinprocess}). + */ +class JnaNativeBindingTest { + + // ------------------------------------------------------------------------- + // Stub CopilotRuntimeLibrary for delegation tests + // ------------------------------------------------------------------------- + + /** + * Minimal stub for testing {@link JnaNativeBinding} delegation without disk + * I/O. + */ + private static class StubRuntimeLibrary implements JnaNativeBinding.CopilotRuntimeLibrary { + int hostStartReturn = 1; + byte hostShutdownReturn = 1; + int connectionOpenReturn = 1; + byte connectionWriteReturn = 1; + byte connectionCloseReturn = 1; + + byte[] lastArgvJson; + int lastArgvJsonLen; + int lastServerId; + int lastConnectionId; + OutboundCallback lastCallback; + + @Override + public int copilot_runtime_host_start(byte[] argvJson, SizeT argvJsonLen, byte[] envJson, SizeT envJsonLen) { + lastArgvJson = argvJson; + lastArgvJsonLen = argvJsonLen.intValue(); + return hostStartReturn; + } + + @Override + public byte copilot_runtime_host_shutdown(int serverId) { + lastServerId = serverId; + return hostShutdownReturn; + } + + @Override + public int copilot_runtime_connection_open(int serverId, OutboundCallback callback, Pointer userData, + byte[] extSource, SizeT extSourceLen, byte[] extName, SizeT extNameLen, byte[] connToken, + SizeT connTokenLen) { + lastServerId = serverId; + lastCallback = callback; + return connectionOpenReturn; + } + + @Override + public byte copilot_runtime_connection_write(int connectionId, byte[] data, SizeT dataLen) { + lastConnectionId = connectionId; + return connectionWriteReturn; + } + + @Override + public byte copilot_runtime_connection_close(int connectionId) { + lastConnectionId = connectionId; + return connectionCloseReturn; + } + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private static Path resolveNativeLib() { + try { + return NativeRuntimeLoader.resolve(); + } catch (Exception e) { + return null; + } + } + + @AfterEach + void resetStaticState() { + JnaNativeBinding.resetForTesting(); + } + + // ========================================================================= + // Delegation via testing constructor (stub — no disk I/O) + // ========================================================================= + + @Test + void hostStartDelegatesToLibraryAndReturnsHandle() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.hostStartReturn = 77; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + byte[] argv = "[\"copilot\"]".getBytes(StandardCharsets.UTF_8); + int result = binding.hostStart(argv, argv.length, null, 0); + + assertEquals(77, result, "hostStart should return the stub's configured value"); + assertEquals(argv, stub.lastArgvJson, "argv bytes should be passed through unchanged"); + assertEquals(argv.length, stub.lastArgvJsonLen); + } + + @Test + void hostStartReturnsZeroOnFailure() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.hostStartReturn = 0; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + byte[] argv = "[\"copilot\"]".getBytes(StandardCharsets.UTF_8); + assertEquals(0, binding.hostStart(argv, argv.length, null, 0), "hostStart must return 0 to signal failure"); + } + + @Test + void hostShutdownDelegatesToLibrary() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.hostShutdownReturn = 1; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + assertTrue(binding.hostShutdown(42)); + assertEquals(42, stub.lastServerId); + } + + @Test + void hostShutdownReturnsFalseOnFailure() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.hostShutdownReturn = 0; + JnaNativeBinding binding = new JnaNativeBinding(stub); + assertFalse(binding.hostShutdown(1)); + } + + @Test + void connectionOpenDelegatesToLibrary() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionOpenReturn = 55; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + OutboundCallback noop = (ud, data, len) -> { + }; + int connId = binding.connectionOpen(42, noop, Pointer.NULL, null, 0, null, 0, null, 0); + + assertEquals(55, connId, "connectionOpen should return the stub's configured handle"); + assertEquals(42, stub.lastServerId); + } + + @Test + void connectionOpenReturnsZeroOnFailure() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionOpenReturn = 0; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + OutboundCallback noop = (ud, data, len) -> { + }; + assertEquals(0, binding.connectionOpen(1, noop, Pointer.NULL, null, 0, null, 0, null, 0), + "connectionOpen must return 0 to signal failure"); + } + + @Test + void connectionWriteDelegatesToLibrary() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionWriteReturn = 1; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + byte[] data = "hello".getBytes(StandardCharsets.UTF_8); + assertTrue(binding.connectionWrite(7, data, data.length)); + assertEquals(7, stub.lastConnectionId); + } + + @Test + void connectionWriteReturnsFalseOnFailure() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionWriteReturn = 0; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + byte[] data = "x".getBytes(StandardCharsets.UTF_8); + assertFalse(binding.connectionWrite(1, data, data.length), + "connectionWrite must propagate false return from the library"); + } + + @Test + void connectionCloseDelegatesToLibrary() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionCloseReturn = 1; + JnaNativeBinding binding = new JnaNativeBinding(stub); + assertTrue(binding.connectionClose(7)); + assertEquals(7, stub.lastConnectionId); + } + + @Test + void connectionCloseReturnsFalseOnFailure() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionCloseReturn = 0; + JnaNativeBinding binding = new JnaNativeBinding(stub); + assertFalse(binding.connectionClose(1)); + } + + @Test + void activeCallbacksStartsAtZero() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + JnaNativeBinding binding = new JnaNativeBinding(stub); + assertEquals(0, binding.activeCallbacks.get(), "Active callback counter must start at zero"); + } + + @Test + void callbackWrapperRemainsReachableAfterConnectionClose() throws InterruptedException { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionOpenReturn = 99; + JnaNativeBinding binding = new JnaNativeBinding(stub); + AtomicInteger invocations = new AtomicInteger(); + + WeakReference callbackReference = openAndCloseConnection(binding, stub, + (userData, data, len) -> invocations.incrementAndGet()); + + awaitGarbageCollection(callbackReference); + + OutboundCallback callback = callbackReference.get(); + assertNotNull(callback, "Callback wrapper must remain strongly reachable after connection close"); + callback.invoke(Pointer.NULL, Pointer.NULL, new SizeT(0)); + assertEquals(1, invocations.get(), "A callback queued before close must remain safely invocable"); + } + + private static WeakReference openAndCloseConnection(JnaNativeBinding binding, + StubRuntimeLibrary stub, OutboundCallback callback) { + int connectionId = binding.connectionOpen(1, callback, Pointer.NULL, null, 0, null, 0, null, 0); + assertEquals(99, connectionId); + assertNotNull(stub.lastCallback); + + WeakReference callbackReference = new WeakReference<>(stub.lastCallback); + stub.lastCallback = null; + assertTrue(binding.connectionClose(connectionId)); + return callbackReference; + } + + private static void awaitGarbageCollection(WeakReference reference) throws InterruptedException { + for (int attempt = 0; attempt < 20 && reference.get() != null; attempt++) { + System.gc(); + System.runFinalization(); + Thread.sleep(10); + } + } + + // ========================================================================= + // Duplicate-load guard + // ========================================================================= + + @Test + void loadFromDifferentPathThrowsIllegalState(@TempDir Path tempDir) throws Exception { + Path nativeLib = resolveNativeLib(); + assumeTrue(nativeLib != null, "Native runtime not available (run with -Pinprocess)"); + Path altPath = tempDir.resolve("runtime-copy-alt.node"); + Files.copy(nativeLib, altPath); + + new JnaNativeBinding(nativeLib); + + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> new JnaNativeBinding(altPath)); + + String msg = ex.getMessage(); + assertTrue(msg.contains("already loaded from"), "Diagnostic must mention 'already loaded from', got: " + msg); + assertTrue(msg.contains(nativeLib.toString()), "Diagnostic must contain path A, got: " + msg); + assertTrue(msg.contains(altPath.toString()), "Diagnostic must contain path B, got: " + msg); + } + + @Test + void duplicateLoadDiagnosticMentionsNotSupported(@TempDir Path tempDir) throws Exception { + Path nativeLib = resolveNativeLib(); + assumeTrue(nativeLib != null, "Native runtime not available (run with -Pinprocess)"); + Path altPath = tempDir.resolve("runtime-copy-b.node"); + Files.copy(nativeLib, altPath); + + new JnaNativeBinding(nativeLib); + + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> new JnaNativeBinding(altPath)); + assertTrue(ex.getMessage().contains("not supported"), + "Diagnostic must mention 'not supported', got: " + ex.getMessage()); + } + + @Test + void resetForTestingAllowsReloadFromDifferentPath(@TempDir Path tempDir) throws Exception { + Path nativeLib = resolveNativeLib(); + assumeTrue(nativeLib != null, "Native runtime not available (run with -Pinprocess)"); + Path altPath = tempDir.resolve("runtime-copy-reset.node"); + Files.copy(nativeLib, altPath); + + new JnaNativeBinding(nativeLib); + + JnaNativeBinding.resetForTesting(); + + // After reset, a different path must succeed. + new JnaNativeBinding(altPath); + } + + @Test + void activeCallbackCountIsIncrementedDuringCallback() { + StubRuntimeLibrary stub = new StubRuntimeLibrary(); + stub.connectionOpenReturn = 99; + JnaNativeBinding binding = new JnaNativeBinding(stub); + + AtomicInteger observedDuringCallback = new AtomicInteger(-1); + + OutboundCallback userCallback = (userData, data, len) -> { + // Observe binding.activeCallbacks while inside the callback + observedDuringCallback.set(binding.activeCallbacks.get()); + }; + + binding.connectionOpen(1, userCallback, Pointer.NULL, null, 0, null, 0, null, 0); + + // The stub captured the tracked wrapper — invoke it to trigger tracking + assertNotNull(stub.lastCallback, "Stub must have captured the tracked callback"); + stub.lastCallback.invoke(Pointer.NULL, Pointer.NULL, new SizeT(0)); + + assertEquals(1, observedDuringCallback.get(), "binding.activeCallbacks must be 1 during callback execution"); + assertEquals(0, binding.activeCallbacks.get(), + "binding.activeCallbacks must return to 0 after callback completes"); + } + +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java new file mode 100644 index 000000000..d316d08de --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java @@ -0,0 +1,580 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class NativeRuntimeLoaderTest { + + private static final String TEST_CLASSIFIER = "linux-x64"; + private static final String OTHER_CLASSIFIER = "darwin-arm64"; + private static final String TEST_VERSION = "1.2.3-test"; + private static final String TEST_NATIVE_VERSION = "0.0.1-test"; + private static final byte[] FAKE_BINARY_CONTENT = "fake runtime.node binary content".getBytes(); + private static final byte[] FAKE_CLI_CONTENT = "fake copilot CLI content".getBytes(); + private static final byte[] OTHER_BINARY_CONTENT = "other runtime.node binary content".getBytes(); + private static final byte[] OTHER_CLI_CONTENT = "other copilot CLI content".getBytes(); + + // ------------------------------------------------------------------------- + // Version properties resource reading + // ------------------------------------------------------------------------- + + @Test + void readVersionReturnsVersionFromPropertiesResource(@TempDir Path tempDir) throws Exception { + ClassLoader loader = classLoaderWithVersionResource(tempDir, "1.0.5-preview"); + assertEquals("1.0.5-preview", NativeRuntimeLoader.readVersion(loader)); + } + + @Test + void readVersionThrowsWhenResourceMissing() { + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); + IllegalStateException ex = assertThrows(IllegalStateException.class, + () -> NativeRuntimeLoader.readVersion(emptyLoader)); + assertTrue(ex.getMessage().contains(NativeRuntimeLoader.VERSION_RESOURCE)); + } + + @Test + void readVersionThrowsWhenVersionPropertyIsBlank(@TempDir Path tempDir) throws Exception { + ClassLoader loader = classLoaderWithVersionResource(tempDir, " "); + IllegalStateException ex = assertThrows(IllegalStateException.class, + () -> NativeRuntimeLoader.readVersion(loader)); + assertTrue(ex.getMessage().contains("version")); + } + + // ------------------------------------------------------------------------- + // COPILOT_CLI_PATH override + // ------------------------------------------------------------------------- + + @Test + void resolveFromCliPathReturnsSiblingWhenRuntimeNodeExists(@TempDir Path tempDir) throws Exception { + Path fakeCliPath = tempDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path runtimeNode = tempDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + Path result = NativeRuntimeLoader.resolveFromCliPath(fakeCliPath.toString()); + + assertEquals(runtimeNode, result); + } + + @Test + void resolveFromCliPathReturnsNullWhenRuntimeNodeMissing(@TempDir Path tempDir) throws Exception { + Path fakeCliPath = tempDir.resolve("copilot"); + Files.createFile(fakeCliPath); + + assertNull(NativeRuntimeLoader.resolveFromCliPath(fakeCliPath.toString())); + } + + @Test + void resolveFromCliPathReturnsNullWhenEnvIsNull() throws Exception { + assertNull(NativeRuntimeLoader.resolveFromCliPath(null)); + } + + @Test + void resolveFromCliPathReturnsNullWhenEnvIsBlank() throws Exception { + assertNull(NativeRuntimeLoader.resolveFromCliPath(" ")); + } + + @Test + void resolveFromCliPathReturnsNullWhenRuntimeNodeIsEmpty(@TempDir Path tempDir) throws Exception { + Path fakeCliPath = tempDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path runtimeNode = tempDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.createFile(runtimeNode); // empty file + + assertNull(NativeRuntimeLoader.resolveFromCliPath(fakeCliPath.toString())); + } + + @Test + void resolveFromCliPathReturnsPrebuildsPathWhenFlatRuntimeNodeIsMissing(@TempDir Path tempDir) throws Exception { + Path fakeCliPath = tempDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path prebuiltDir = tempDir.resolve("prebuilds").resolve(PlatformDetector.detectClassifier()); + Files.createDirectories(prebuiltDir); + Path runtimeNode = prebuiltDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + Path result = NativeRuntimeLoader.resolveFromCliPath(fakeCliPath.toString()); + + assertEquals(runtimeNode, result); + } + + @Test + void resolveFromCliPathPrefersFlatRuntimeNodeOverPrebuildsPath(@TempDir Path tempDir) throws Exception { + Path fakeCliPath = tempDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path flatRuntimeNode = tempDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(flatRuntimeNode, FAKE_BINARY_CONTENT); + Path prebuiltDir = tempDir.resolve("prebuilds").resolve(PlatformDetector.detectClassifier()); + Files.createDirectories(prebuiltDir); + Files.write(prebuiltDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), OTHER_BINARY_CONTENT); + + Path result = NativeRuntimeLoader.resolveFromCliPath(fakeCliPath.toString()); + + assertEquals(flatRuntimeNode, result); + } + + @Test + void resolveEntrypointUsesConfiguredCliWhenRuntimeIsInPrebuilds(@TempDir Path tempDir) throws Exception { + Path cli = Files.writeString(tempDir.resolve("copilot"), "fake cli"); + Path runtimeDir = tempDir.resolve("prebuilds").resolve(PlatformDetector.detectClassifier()); + Files.createDirectories(runtimeDir); + Path runtime = Files.write(runtimeDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), FAKE_BINARY_CONTENT); + + assertEquals(cli, NativeRuntimeLoader.resolveEntrypoint(cli.toString(), runtime)); + } + + @Test + void resolveFromCliPathReturnsAbsolutePathForRelativeCliPath(@TempDir Path tempDir) throws Exception { + Path workingDirectory = Path.of("").toAbsolutePath(); + Path fakeCliDir = tempDir.resolve("cli-dir"); + Files.createDirectories(fakeCliDir); + Path fakeCliPath = fakeCliDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path runtimeNode = fakeCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + Path relativeCliPath = workingDirectory.relativize(fakeCliPath); + + assertEquals(runtimeNode, NativeRuntimeLoader.resolveFromCliPath(relativeCliPath.toString())); + } + + @Test + void cliPathOverrideTakesPriorityOverClasspathExtraction(@TempDir Path tempDir) throws Exception { + // Create a valid runtime.node alongside the fake CLI path + Path fakeCliDir = tempDir.resolve("cli-dir"); + Files.createDirectories(fakeCliDir); + Path fakeCliPath = fakeCliDir.resolve("copilot"); + Files.createFile(fakeCliPath); + Path runtimeNode = fakeCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + // Source 2 is also available (should be ignored) + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.resolve(fakeCliPath.toString(), cacheBase, loader, TEST_CLASSIFIER, + TEST_VERSION); + + assertEquals(runtimeNode, result, "Source 1 (COPILOT_CLI_PATH) must take priority over classpath extraction"); + } + + // ------------------------------------------------------------------------- + // Source 2: classpath extraction to cache + // ------------------------------------------------------------------------- + + @Test + void extractToCacheCopiesResourceToVersionedCacheDirectory(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + Path expected = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER) + .resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + assertEquals(expected, result); + assertTrue(Files.isRegularFile(result)); + assertTrue(Files.size(result) > 0); + } + + @Test + void extractToCacheReturnsCachedFileOnSecondCall(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path first = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + long modifiedAfterFirstExtraction = Files.getLastModifiedTime(first).toMillis(); + + // Small delay so modification time would differ if the file were rewritten + Thread.sleep(50); + + Path second = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + long modifiedAfterSecondCall = Files.getLastModifiedTime(second).toMillis(); + + assertEquals(first, second); + assertEquals(modifiedAfterFirstExtraction, modifiedAfterSecondCall, + "Cached file must not be overwritten on cache hit"); + } + + @Test + void changedNativeVersionDoesNotReuseCachedArtifactsForSameSdkVersion(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader firstLoader = classLoaderWithNativeArtifacts(tempDir.resolve("native-v1"), TEST_CLASSIFIER, "1.0.0", + FAKE_BINARY_CONTENT, FAKE_CLI_CONTENT); + ClassLoader secondLoader = classLoaderWithNativeArtifacts(tempDir.resolve("native-v2"), TEST_CLASSIFIER, + "1.1.0", OTHER_BINARY_CONTENT, OTHER_CLI_CONTENT); + + Path firstRuntime = NativeRuntimeLoader.extractToCache(cacheBase, firstLoader, TEST_CLASSIFIER, TEST_VERSION); + Path secondRuntime = NativeRuntimeLoader.extractToCache(cacheBase, secondLoader, TEST_CLASSIFIER, TEST_VERSION); + + assertNotEquals(firstRuntime, secondRuntime, "Different native versions must use different cache entries"); + assertBytesEqual(FAKE_BINARY_CONTENT, Files.readAllBytes(firstRuntime)); + assertBytesEqual(FAKE_CLI_CONTENT, + Files.readAllBytes(firstRuntime.getParent().resolve(NativeRuntimeLoader.CLI_FILENAME))); + assertBytesEqual(OTHER_BINARY_CONTENT, Files.readAllBytes(secondRuntime)); + assertBytesEqual(OTHER_CLI_CONTENT, + Files.readAllBytes(secondRuntime.getParent().resolve(NativeRuntimeLoader.CLI_FILENAME))); + } + + @Test + void extractToCacheThrowsWhenClasspathResourceMissing(@TempDir Path tempDir) { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); + + assertThrows(IOException.class, + () -> NativeRuntimeLoader.extractToCache(cacheBase, emptyLoader, TEST_CLASSIFIER, TEST_VERSION)); + } + + @Test + void extractToCacheThrowsWhenNativeMetadataMissing(@TempDir Path tempDir) throws Exception { + Path resourceDir = tempDir.resolve("native").resolve(TEST_CLASSIFIER); + Files.createDirectories(resourceDir); + Files.write(resourceDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), FAKE_BINARY_CONTENT); + ClassLoader loader = new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + + IOException ex = assertThrows(IOException.class, () -> NativeRuntimeLoader + .extractToCache(tempDir.resolve("cache"), loader, TEST_CLASSIFIER, TEST_VERSION)); + + assertTrue(ex.getMessage().contains("platform.properties")); + } + + @Test + void extractedBinaryContentsMatchClasspathResource(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + byte[] extracted = Files.readAllBytes(result); + assertBytesEqual(FAKE_BINARY_CONTENT, extracted); + } + + @Test + void extractToCacheFiltersClasspathByClassifier(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + writeRuntimeResource(tempDir, TEST_CLASSIFIER, FAKE_BINARY_CONTENT); + writeRuntimeResource(tempDir, OTHER_CLASSIFIER, OTHER_BINARY_CONTENT); + ClassLoader loader = new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + + Path result = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + assertTrue(result.toString().contains(TEST_CLASSIFIER), "Cache path must include the classifier: " + result); + assertBytesEqual(FAKE_BINARY_CONTENT, Files.readAllBytes(result)); + } + + @Test + void extractToCacheRepairsInvalidCacheEntry(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + Path cached = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER) + .resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.createDirectories(cached.getParent()); + Files.createFile(cached); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + assertEquals(cached, result); + assertBytesEqual(FAKE_BINARY_CONTENT, Files.readAllBytes(result)); + } + + @Test + void nonExecutableCachedCliIsNotAcceptedAsValid(@TempDir Path tempDir) throws Exception { + assumeTrue(Files.getFileStore(tempDir).supportsFileAttributeView("posix")); + Path cacheBase = tempDir.resolve("cache"); + Path cacheDir = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER); + Files.createDirectories(cacheDir); + Files.write(cacheDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), FAKE_BINARY_CONTENT); + Path cachedCli = Files.write(cacheDir.resolve(NativeRuntimeLoader.CLI_FILENAME), FAKE_CLI_CONTENT); + Files.setPosixFilePermissions(cachedCli, PosixFilePermissions.fromString("rw-------")); + ClassLoader loader = classLoaderWithRuntimeAndCliResources(tempDir, TEST_CLASSIFIER); + + NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + assertTrue(Files.isExecutable(cachedCli), "A non-executable cached CLI must be repaired or replaced"); + } + + // ------------------------------------------------------------------------- + // Source 3: bundled-CLI sibling + // ------------------------------------------------------------------------- + + @Test + void bundledCliSiblingIsUsedWhenClasspathResourceAbsent(@TempDir Path tempDir) throws Exception { + Path bundledCliDir = tempDir.resolve("bundled-cli"); + Files.createDirectories(bundledCliDir); + Path runtimeNode = bundledCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + Path cacheBase = tempDir.resolve("cache"); + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); // no classpath resource + + Path result = NativeRuntimeLoader.resolve(null, cacheBase, emptyLoader, TEST_CLASSIFIER, TEST_VERSION, + bundledCliDir); + + assertEquals(runtimeNode, result, + "Source 3 (bundled-CLI sibling) must be used when classpath resource is absent"); + } + + @Test + void classpathResourceWinsOverBundledCliSibling(@TempDir Path tempDir) throws Exception { + // Source 3: bundled CLI dir with runtime.node (should NOT win) + Path bundledCliDir = tempDir.resolve("bundled-cli"); + Files.createDirectories(bundledCliDir); + Files.write(bundledCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), "bundled".getBytes()); + + // Source 2: classpath resource (should win) + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.resolve(null, cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION, + bundledCliDir); + + Path expectedFromClasspath = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION) + .resolve(TEST_CLASSIFIER).resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + assertEquals(expectedFromClasspath, result, + "Source 2 (classpath) must win over source 3 (bundled-CLI sibling)"); + assertNotEquals(bundledCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), result); + } + + @Test + void bundledCliSiblingIsIgnoredWhenRuntimeNodeMissing(@TempDir Path tempDir) { + Path bundledCliDir = tempDir.resolve("bundled-cli-no-runtime"); + // bundledCliDir doesn't even exist — no runtime.node present + + Path cacheBase = tempDir.resolve("cache"); + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); + + // Both source 2 and source 3 absent: must throw (the classpath error) + IOException ex = assertThrows(IOException.class, () -> NativeRuntimeLoader.resolve(null, cacheBase, emptyLoader, + TEST_CLASSIFIER, TEST_VERSION, bundledCliDir)); + assertTrue(ex.getMessage().contains("classpath"), "Error should mention classpath: " + ex.getMessage()); + } + + // ------------------------------------------------------------------------- + // Atomic publication test seam + // ------------------------------------------------------------------------- + + @Test + void defaultPublisherMovesSourceToTarget(@TempDir Path tempDir) throws Exception { + Path temp = Files.createTempFile(tempDir, "runtime-tmp-", ".node"); + Files.write(temp, FAKE_BINARY_CONTENT); + Path target = tempDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + + NativeRuntimeLoader.DEFAULT_PUBLISHER.publish(temp, target); + + assertTrue(Files.isRegularFile(target), "Target must exist after publication"); + assertTrue(Files.size(target) > 0, "Target must be non-empty"); + assertFalse(Files.exists(temp), "Source temp file must be absent after atomic move"); + } + + @Test + void cliIsExecutableBeforeAtomicPublication(@TempDir Path tempDir) throws Exception { + assumeTrue(Files.getFileStore(tempDir).supportsFileAttributeView("posix")); + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeAndCliResources(tempDir, TEST_CLASSIFIER); + NativeRuntimeLoader.AtomicPublisher publisher = (temp, cached) -> { + if (cached.getFileName().toString().equals(NativeRuntimeLoader.CLI_FILENAME)) { + assertTrue(Files.isExecutable(temp), "CLI temp file must be executable before atomic publication"); + } + Files.move(temp, cached, StandardCopyOption.REPLACE_EXISTING); + }; + + NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION, publisher); + } + + @Test + void extractionCleansUpTempFileWhenPublicationFails(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + // Capture the temp path so we can verify it was deleted + Path[] capturedTemp = {null}; + NativeRuntimeLoader.AtomicPublisher failingPublisher = (temp, cached) -> { + capturedTemp[0] = temp; + throw new AtomicMoveNotSupportedException(temp.toString(), cached.toString(), + "filesystem does not support atomic moves — test"); + }; + + assertThrows(AtomicMoveNotSupportedException.class, () -> NativeRuntimeLoader.extractToCache(cacheBase, loader, + TEST_CLASSIFIER, TEST_VERSION, failingPublisher)); + + assertNotNull(capturedTemp[0], "Publisher must have been invoked"); + assertFalse(Files.exists(capturedTemp[0]), "Temp file must be deleted after failed publication"); + } + + @Test + void extractionCleansUpTempFileWhenPublisherThrowsIllegalStateException(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path[] capturedTemp = {null}; + NativeRuntimeLoader.AtomicPublisher unsupportedPublisher = (temp, cached) -> { + capturedTemp[0] = temp; + // Simulate the wrapping that DEFAULT_PUBLISHER performs for + // AtomicMoveNotSupportedException + throw new IllegalStateException("Filesystem does not support atomic moves; cannot safely publish " + + NativeRuntimeLoader.RUNTIME_FILENAME + " to " + cached); + }; + + IllegalStateException ex = assertThrows(IllegalStateException.class, () -> NativeRuntimeLoader + .extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION, unsupportedPublisher)); + + assertTrue(ex.getMessage().contains("atomic moves"), + "Error message should describe the atomic-move failure: " + ex.getMessage()); + assertNotNull(capturedTemp[0], "Publisher must have been invoked"); + assertFalse(Files.exists(capturedTemp[0]), "Temp file must be deleted after failed atomic publication"); + } + + // ------------------------------------------------------------------------- + // Concurrent extraction safety + // ------------------------------------------------------------------------- + + @Test + void concurrentExtractionByMultipleThreadsBothSucceed(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + int threadCount = 8; + CountDownLatch startGate = new CountDownLatch(1); + ExecutorService pool = Executors.newFixedThreadPool(threadCount); + List> futures = new ArrayList<>(); + + for (int i = 0; i < threadCount; i++) { + futures.add(pool.submit(() -> { + startGate.await(); + return NativeRuntimeLoader.extractToCache(cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + })); + } + + startGate.countDown(); + pool.shutdown(); + assertTrue(pool.awaitTermination(10, TimeUnit.SECONDS)); + + Path expected = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER) + .resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + for (Future future : futures) { + Path result = future.get(); + assertEquals(expected, result); + assertTrue(Files.isRegularFile(result)); + assertTrue(Files.size(result) > 0); + } + try (var files = Files.list(expected.getParent())) { + assertEquals(List.of(expected), files.toList(), "Concurrent extraction must clean up temporary files"); + } + } + + // ------------------------------------------------------------------------- + // resolve() -- full three-source resolution chain + // ------------------------------------------------------------------------- + + @Test + void resolveWithNullCliEnvExtractsFromClasspath(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); + + Path result = NativeRuntimeLoader.resolve(null, cacheBase, loader, TEST_CLASSIFIER, TEST_VERSION); + + assertNotNull(result); + assertTrue(Files.isRegularFile(result)); + assertTrue(Files.size(result) > 0); + } + + @Test + void resolveThrowsWhenNoSourceIsAvailable(@TempDir Path tempDir) { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); + + // No CLI env, no classpath resource, no bundled-CLI dir → throw + assertThrows(IOException.class, + () -> NativeRuntimeLoader.resolve(null, cacheBase, emptyLoader, TEST_CLASSIFIER, TEST_VERSION)); + } + + @Test + void resolveFallsBackToRuntimeAlongsideBundledCli(@TempDir Path tempDir) throws Exception { + Path cacheBase = tempDir.resolve("cache"); + ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); + Path bundledCli = tempDir.resolve("copilot"); + Files.createFile(bundledCli); + Path runtimeNode = tempDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); + Files.write(runtimeNode, FAKE_BINARY_CONTENT); + + Path result = NativeRuntimeLoader.resolve(null, bundledCli.toString(), cacheBase, emptyLoader, TEST_CLASSIFIER, + TEST_VERSION); + + assertEquals(runtimeNode, result); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + private static ClassLoader classLoaderWithVersionResource(Path tempDir, String version) throws IOException { + Path propsFile = tempDir.resolve(NativeRuntimeLoader.VERSION_RESOURCE); + Files.writeString(propsFile, "version=" + version + "\n"); + return new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + } + + private static ClassLoader classLoaderWithRuntimeResource(Path tempDir, String classifier) throws IOException { + writeRuntimeResource(tempDir, classifier, FAKE_BINARY_CONTENT); + return new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + } + + private static ClassLoader classLoaderWithRuntimeAndCliResources(Path tempDir, String classifier) + throws IOException { + writeRuntimeResource(tempDir, classifier, FAKE_BINARY_CONTENT); + Path resourceDir = tempDir.resolve("native").resolve(classifier); + Files.write(resourceDir.resolve(NativeRuntimeLoader.CLI_FILENAME), FAKE_CLI_CONTENT); + return new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + } + + private static ClassLoader classLoaderWithNativeArtifacts(Path tempDir, String classifier, String nativeVersion, + byte[] runtimeContent, byte[] cliContent) throws IOException { + writeRuntimeResource(tempDir, classifier, runtimeContent); + Path resourceDir = tempDir.resolve("native").resolve(classifier); + Files.write(resourceDir.resolve(NativeRuntimeLoader.CLI_FILENAME), cliContent); + Files.writeString(resourceDir.resolve("platform.properties"), + "classifier=" + classifier + "\nversion=" + nativeVersion + "\n"); + return new URLClassLoader(new URL[]{tempDir.toUri().toURL()}, null); + } + + private static void writeRuntimeResource(Path tempDir, String classifier, byte[] content) throws IOException { + Path resourceDir = tempDir.resolve("native").resolve(classifier); + Files.createDirectories(resourceDir); + Files.write(resourceDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), content); + Files.writeString(resourceDir.resolve("platform.properties"), + "classifier=" + classifier + "\nversion=" + TEST_NATIVE_VERSION + "\n"); + } + + private static void assertBytesEqual(byte[] expected, byte[] actual) { + assertEquals(expected.length, actual.length, "Array lengths differ"); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], actual[i], "Byte differs at index " + i); + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/PlatformDetectorTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/PlatformDetectorTest.java new file mode 100644 index 000000000..82049ea6a --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/PlatformDetectorTest.java @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class PlatformDetectorTest { + + @Test + void detectOsMapsSupportedNames() { + withSystemProperty("os.name", "Mac OS X", () -> assertEquals("darwin", PlatformDetector.detectOs())); + withSystemProperty("os.name", "Darwin", () -> assertEquals("darwin", PlatformDetector.detectOs())); + withSystemProperty("os.name", "Windows 11", () -> assertEquals("win32", PlatformDetector.detectOs())); + withSystemProperty("os.name", "Linux", () -> assertEquals("linux", PlatformDetector.detectOs())); + } + + @Test + void detectOsThrowsForUnsupportedSystem() { + withSystemProperty("os.name", "Solaris", + () -> assertThrows(IllegalStateException.class, PlatformDetector::detectOs)); + } + + @Test + void detectArchMapsSupportedAliases() { + withSystemProperty("os.arch", "amd64", () -> assertEquals("x64", PlatformDetector.detectArch())); + withSystemProperty("os.arch", "x86_64", () -> assertEquals("x64", PlatformDetector.detectArch())); + withSystemProperty("os.arch", "x64", () -> assertEquals("x64", PlatformDetector.detectArch())); + withSystemProperty("os.arch", "aarch64", () -> assertEquals("arm64", PlatformDetector.detectArch())); + withSystemProperty("os.arch", "arm64", () -> assertEquals("arm64", PlatformDetector.detectArch())); + } + + @Test + void detectArchThrowsForUnsupportedArchitecture() { + withSystemProperty("os.arch", "ppc64", + () -> assertThrows(IllegalStateException.class, PlatformDetector::detectArch)); + } + + @Test + void detectLinuxLibcParsesGlibcInterpPath() throws Exception { + byte[] glibcProbe = buildElf64ProbeWithInterp("/lib64/ld-linux-x86-64.so.2"); + assertEquals(PlatformDetector.LinuxLibc.GLIBC, PlatformDetector.detectLinuxLibc(glibcProbe)); + } + + @Test + void detectLinuxLibcParsesMuslInterpPath() throws Exception { + byte[] muslProbe = buildElf64ProbeWithInterp("/lib/ld-musl-x86_64.so.1"); + assertEquals(PlatformDetector.LinuxLibc.MUSL, PlatformDetector.detectLinuxLibc(muslProbe)); + } + + @Test + void detectLinuxLibcOnLinuxReturnsRecognizedValue() { + withSystemProperty("os.name", "Linux", () -> { + PlatformDetector.LinuxLibc libc = PlatformDetector.detectLinuxLibc(); + assertTrue(libc == PlatformDetector.LinuxLibc.GLIBC || libc == PlatformDetector.LinuxLibc.MUSL + || libc == PlatformDetector.LinuxLibc.UNKNOWN); + }); + } + + @Test + void detectLinuxLibcReturnsUnknownOutsideLinux() { + withSystemProperty("os.name", "Windows 11", + () -> assertEquals(PlatformDetector.LinuxLibc.UNKNOWN, PlatformDetector.detectLinuxLibc())); + } + + @Test + void detectClassifierReturnsClassifierForCurrentLinuxLibc() { + PlatformDetector.LinuxLibc libc = PlatformDetector.detectLinuxLibc(); + String expected = libc == PlatformDetector.LinuxLibc.MUSL ? "linuxmusl-x64" : "linux-x64"; + + withSystemProperties("Linux", "amd64", () -> assertEquals(expected, PlatformDetector.detectClassifier())); + } + + @Test + void detectClassifierAllowListCoversAllSupportedValues() { + Set expected = Set.of("linux-x64", "linux-arm64", "linuxmusl-x64", "linuxmusl-arm64", "darwin-x64", + "darwin-arm64", "win32-x64", "win32-arm64"); + assertEquals(expected, PlatformDetector.supportedClassifiers()); + + Set resolved = new LinkedHashSet<>(); + resolved.add(PlatformDetector.detectClassifier("linux", "x64", PlatformDetector.LinuxLibc.GLIBC)); + resolved.add(PlatformDetector.detectClassifier("linux", "arm64", PlatformDetector.LinuxLibc.GLIBC)); + resolved.add(PlatformDetector.detectClassifier("linux", "x64", PlatformDetector.LinuxLibc.MUSL)); + resolved.add(PlatformDetector.detectClassifier("linux", "arm64", PlatformDetector.LinuxLibc.MUSL)); + resolved.add(PlatformDetector.detectClassifier("darwin", "x64", PlatformDetector.LinuxLibc.UNKNOWN)); + resolved.add(PlatformDetector.detectClassifier("darwin", "arm64", PlatformDetector.LinuxLibc.UNKNOWN)); + resolved.add(PlatformDetector.detectClassifier("win32", "x64", PlatformDetector.LinuxLibc.UNKNOWN)); + resolved.add(PlatformDetector.detectClassifier("win32", "arm64", PlatformDetector.LinuxLibc.UNKNOWN)); + + assertEquals(expected, resolved); + } + + @Test + void detectClassifierFailsFastForUnsupportedTuple() { + assertThrows(IllegalStateException.class, + () -> PlatformDetector.detectClassifier("darwin", "mips64", PlatformDetector.LinuxLibc.UNKNOWN)); + } + + @Test + void detectClassifierFailsForUnsupportedCurrentPlatform() { + withSystemProperties("Solaris", "amd64", + () -> assertThrows(IllegalStateException.class, PlatformDetector::detectClassifier)); + } + + @Test + void detectLinuxLibcReturnsUnknownWhenElfParsingFails() { + byte[] invalidProbe = new byte[64]; + Arrays.fill(invalidProbe, (byte) 1); + + assertThrows(IOException.class, () -> PlatformDetector.detectLinuxLibc(invalidProbe)); + } + + @Test + void detectLinuxLibcReturnsUnknownForTruncatedProgramHeader(@TempDir Path tempDir) throws IOException { + byte[] malformedProbe = buildElf64ProbeWithInterp("/lib64/ld-linux-x86-64.so.2"); + writeLe64(malformedProbe, 32, malformedProbe.length - 1); + writeLe16(malformedProbe, 54, 1); + Path executable = tempDir.resolve("malformed-elf"); + Files.write(executable, malformedProbe); + + assertEquals(PlatformDetector.LinuxLibc.UNKNOWN, PlatformDetector.detectLinuxLibc(executable)); + } + + private static void withSystemProperties(String osName, String osArch, Runnable action) { + String previousOsName = System.getProperty("os.name"); + String previousOsArch = System.getProperty("os.arch"); + try { + System.setProperty("os.name", osName); + System.setProperty("os.arch", osArch); + action.run(); + } finally { + restoreProperty("os.name", previousOsName); + restoreProperty("os.arch", previousOsArch); + } + } + + private static void withSystemProperty(String key, String value, Runnable action) { + String previousValue = System.getProperty(key); + try { + System.setProperty(key, value); + action.run(); + } finally { + restoreProperty(key, previousValue); + } + } + + private static void restoreProperty(String key, String value) { + if (value == null) { + System.clearProperty(key); + } else { + System.setProperty(key, value); + } + } + + private static byte[] buildElf64ProbeWithInterp(String interpreterPath) { + byte[] interpBytes = interpreterPath.getBytes(StandardCharsets.UTF_8); + byte[] probe = new byte[512]; + + probe[0] = 0x7F; + probe[1] = 'E'; + probe[2] = 'L'; + probe[3] = 'F'; + probe[4] = 2; + probe[5] = 1; + + int phoff = 64; + int phentsize = 56; + int phnum = 1; + int interpOffset = 256; + int interpSize = interpBytes.length + 1; + + writeLe64(probe, 32, phoff); + writeLe16(probe, 54, phentsize); + writeLe16(probe, 56, phnum); + + int pHeader = phoff; + writeLe32(probe, pHeader, 3); + writeLe64(probe, pHeader + 8, interpOffset); + writeLe64(probe, pHeader + 32, interpSize); + + System.arraycopy(interpBytes, 0, probe, interpOffset, interpBytes.length); + probe[interpOffset + interpBytes.length] = 0; + return probe; + } + + private static void writeLe16(byte[] buffer, int offset, int value) { + buffer[offset] = (byte) (value & 0xFF); + buffer[offset + 1] = (byte) ((value >>> 8) & 0xFF); + } + + private static void writeLe32(byte[] buffer, int offset, int value) { + buffer[offset] = (byte) (value & 0xFF); + buffer[offset + 1] = (byte) ((value >>> 8) & 0xFF); + buffer[offset + 2] = (byte) ((value >>> 16) & 0xFF); + buffer[offset + 3] = (byte) ((value >>> 24) & 0xFF); + } + + private static void writeLe64(byte[] buffer, int offset, long value) { + for (int i = 0; i < 8; i++) { + buffer[offset + i] = (byte) ((value >>> (8 * i)) & 0xFF); + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/QueueInputStreamTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/QueueInputStreamTest.java new file mode 100644 index 000000000..6fd961670 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ffi/QueueInputStreamTest.java @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.ffi; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +class QueueInputStreamTest { + + @Test + void readReturnsEnqueuedBytesAcrossMultipleChunks() throws Exception { + QueueInputStream stream = new QueueInputStream(); + stream.enqueue("hello ".getBytes(StandardCharsets.UTF_8)); + stream.enqueue("world".getBytes(StandardCharsets.UTF_8)); + + byte[] buffer = new byte[11]; + int first = stream.read(buffer, 0, 6); + int second = stream.read(buffer, 6, 5); + + assertEquals(6, first); + assertEquals(5, second); + assertArrayEquals("hello world".getBytes(StandardCharsets.UTF_8), buffer); + } + + @Test + void readBlocksUntilDataArrives() throws Exception { + QueueInputStream stream = new QueueInputStream(); + + CompletableFuture readFuture = CompletableFuture.supplyAsync(() -> { + try { + return stream.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + Thread.sleep(100); + stream.enqueue(new byte[]{(byte) 'A'}); + + assertEquals((int) 'A', readFuture.get(2, TimeUnit.SECONDS)); + } + + @Test + void closeSignalsEndOfStream() throws Exception { + QueueInputStream stream = new QueueInputStream(); + stream.enqueue("x".getBytes(StandardCharsets.UTF_8)); + + assertEquals('x', stream.read()); + stream.close(); + assertEquals(-1, stream.read()); + } + + @Test + void closeUnblocksPendingReadWithEof() throws Exception { + QueueInputStream stream = new QueueInputStream(); + + CompletableFuture readFuture = CompletableFuture.supplyAsync(() -> { + try { + return stream.read(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + Thread.sleep(100); + stream.close(); + + assertEquals(-1, readFuture.get(2, TimeUnit.SECONDS)); + assertTrue(readFuture.isDone()); + } +} diff --git a/java/src/test/java/com/github/copilot/generated/GeneratedEventTypesCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/GeneratedEventTypesCoverageTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/generated/GeneratedEventTypesCoverageTest.java rename to java/sdk/src/test/java/com/github/copilot/generated/GeneratedEventTypesCoverageTest.java diff --git a/java/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java b/java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java rename to java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java rename to java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java diff --git a/java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java rename to java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcRecordsCoverageTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/ParamCoercionTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/ParamCoercionTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/ParamCoercionTest.java rename to java/sdk/src/test/java/com/github/copilot/rpc/ParamCoercionTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/ParamSchemaTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/ParamSchemaTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/ParamSchemaTest.java rename to java/sdk/src/test/java/com/github/copilot/rpc/ParamSchemaTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/RecordInvocationArgs.java b/java/sdk/src/test/java/com/github/copilot/rpc/RecordInvocationArgs.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/RecordInvocationArgs.java rename to java/sdk/src/test/java/com/github/copilot/rpc/RecordInvocationArgs.java diff --git a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java rename to java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionFromObjectTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java rename to java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionIsTerminalTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/ToolDefinitionLambdaTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionLambdaTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/ToolDefinitionLambdaTest.java rename to java/sdk/src/test/java/com/github/copilot/rpc/ToolDefinitionLambdaTest.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/ArgCoercionTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DateTimeTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/DefaultValueTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/InvocationAwareTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/MultiReturnTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OptionalParamTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/OverrideTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/SimpleTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticInvocationTools.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticTools$$CopilotToolMeta.java diff --git a/java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools.java b/java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticTools.java similarity index 100% rename from java/src/test/java/com/github/copilot/rpc/fixtures/StaticTools.java rename to java/sdk/src/test/java/com/github/copilot/rpc/fixtures/StaticTools.java diff --git a/java/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java b/java/sdk/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java rename to java/sdk/src/test/java/com/github/copilot/tool/CopilotToolAnnotationTest.java diff --git a/java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java b/java/sdk/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java rename to java/sdk/src/test/java/com/github/copilot/tool/CopilotToolProcessorTest.java diff --git a/java/src/test/java/com/github/copilot/tool/ParamTest.java b/java/sdk/src/test/java/com/github/copilot/tool/ParamTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/tool/ParamTest.java rename to java/sdk/src/test/java/com/github/copilot/tool/ParamTest.java diff --git a/java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java b/java/sdk/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java similarity index 100% rename from java/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java rename to java/sdk/src/test/java/com/github/copilot/tool/SchemaGeneratorTest.java diff --git a/java/src/test/prompts/PROMPT-smoke-test.md b/java/sdk/src/test/prompts/PROMPT-smoke-test.md similarity index 100% rename from java/src/test/prompts/PROMPT-smoke-test.md rename to java/sdk/src/test/prompts/PROMPT-smoke-test.md diff --git a/java/src/test/resources/logging-debug.properties b/java/sdk/src/test/resources/logging-debug.properties similarity index 100% rename from java/src/test/resources/logging-debug.properties rename to java/sdk/src/test/resources/logging-debug.properties diff --git a/java/src/test/resources/logging.properties b/java/sdk/src/test/resources/logging.properties similarity index 100% rename from java/src/test/resources/logging.properties rename to java/sdk/src/test/resources/logging.properties diff --git a/scripts/docs-validation/validate.ts b/scripts/docs-validation/validate.ts index b609ef859..6ce615eab 100644 --- a/scripts/docs-validation/validate.ts +++ b/scripts/docs-validation/validate.ts @@ -385,13 +385,16 @@ async function validateJava(): Promise { fs.copyFileSync(path.join(javaDir, file), path.join(srcDir, file)); } - // Read the SDK version from java/pom.xml - const sdkPomPath = path.join(ROOT_DIR, "java", "pom.xml"); + // Read the inherited SDK version from java/sdk/pom.xml + const sdkPomPath = path.join(ROOT_DIR, "java", "sdk", "pom.xml"); const sdkPomContent = fs.readFileSync(sdkPomPath, "utf-8"); const versionMatch = sdkPomContent.match( - /copilot-sdk-java<\/artifactId>\s*([^<]+)<\/version>/, + /[\s\S]*?([^<]+)<\/version>[\s\S]*?<\/parent>/, ); - const sdkVersion = versionMatch ? versionMatch[1] : "1.0.0-SNAPSHOT"; + if (!versionMatch) { + throw new Error(`Could not read the Java SDK version from ${sdkPomPath}`); + } + const sdkVersion = versionMatch[1]; // Create pom.xml that references the local SDK const pomXml = ` From 4134c5e4312e0b98b9eba678537e9b716a3e437d Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Wed, 12 Aug 2026 16:55:07 -0700 Subject: [PATCH 56/72] fix(java): scope documentation version updates (#2324) --- java/scripts/test-update-documentation-versions.sh | 5 +++++ java/scripts/update-documentation-versions.sh | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/java/scripts/test-update-documentation-versions.sh b/java/scripts/test-update-documentation-versions.sh index 213bda6c4..606e2f7f8 100755 --- a/java/scripts/test-update-documentation-versions.sh +++ b/java/scripts/test-update-documentation-versions.sh @@ -28,6 +28,10 @@ run_case() { " ${old_dev_version}" \ '' \ "implementation 'com.github:copilot-sdk-java:${old_dev_version}'" \ + '' \ + ' jna' \ + ' 5.19.1' \ + '' \ > "${case_dir}/README.md" printf '%s\n' \ "///usr/bin/env jbang \"\$0\" \"\$@\" ; exit \$?" \ @@ -40,6 +44,7 @@ run_case() { grep -Fqx "implementation 'com.github:copilot-sdk-java:${version}'" "${case_dir}/README.md" grep -Fqx " ${dev_version}" "${case_dir}/README.md" grep -Fqx "implementation 'com.github:copilot-sdk-java:${dev_version}'" "${case_dir}/README.md" + grep -Fqx ' 5.19.1' "${case_dir}/README.md" grep -Fqx "//DEPS com.github:copilot-sdk-java:${version}" "${case_dir}/jbang-example.java" if grep -Fq "$old_version" "${case_dir}/README.md" "${case_dir}/jbang-example.java" || diff --git a/java/scripts/update-documentation-versions.sh b/java/scripts/update-documentation-versions.sh index d2d23c4ff..5280f55d5 100755 --- a/java/scripts/update-documentation-versions.sh +++ b/java/scripts/update-documentation-versions.sh @@ -39,9 +39,10 @@ close $input or die "Cannot close $path: $!\n"; # Match accepted release versions plus numeric suffixes left by the former broken updater. my $version = qr/[0-9]+\.[0-9]+\.[0-9]+(?:-(?:preview|(?:beta-)?java(?:-preview)?)\.[0-9]+)?(?:-[0-9]+)*/; -my $snapshot_xml = ($content =~ s{$version-SNAPSHOT}{$ENV{DEV_VERSION}}g); +my $sdk_dependency_version = qr{(copilot-sdk-java(?:(?!).)*?)}s; +my $snapshot_xml = ($content =~ s{$sdk_dependency_version$version-SNAPSHOT}{$1$ENV{DEV_VERSION}}g); my $snapshot_gradle = ($content =~ s{(copilot-sdk-java:)$version-SNAPSHOT(?![-A-Za-z0-9.])}{$1 . $ENV{DEV_VERSION}}ge); -my $release_xml = ($content =~ s{$version}{$ENV{VERSION}}g); +my $release_xml = ($content =~ s{$sdk_dependency_version$version}{$1$ENV{VERSION}}g); my $release_gradle = ($content =~ s{(copilot-sdk-java:)$version(?![-A-Za-z0-9.])}{$1 . $ENV{VERSION}}ge); die "Expected one release and one snapshot example for both Maven and Gradle in $path\n" From 5c2dec4eb7dd6d0abd60c69317954e0c20a70ce6 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Wed, 12 Aug 2026 17:12:24 -0700 Subject: [PATCH 57/72] test(java): skip linux runtime tests on other platforms (#2325) --- .../copilot/ffi/NativeRuntimeLoaderTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java b/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java index d316d08de..d6a9b3481 100644 --- a/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ffi/NativeRuntimeLoaderTest.java @@ -169,6 +169,7 @@ void resolveFromCliPathReturnsAbsolutePathForRelativeCliPath(@TempDir Path tempD @Test void cliPathOverrideTakesPriorityOverClasspathExtraction(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); // Create a valid runtime.node alongside the fake CLI path Path fakeCliDir = tempDir.resolve("cli-dir"); Files.createDirectories(fakeCliDir); @@ -193,6 +194,7 @@ void cliPathOverrideTakesPriorityOverClasspathExtraction(@TempDir Path tempDir) @Test void extractToCacheCopiesResourceToVersionedCacheDirectory(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -207,6 +209,7 @@ void extractToCacheCopiesResourceToVersionedCacheDirectory(@TempDir Path tempDir @Test void extractToCacheReturnsCachedFileOnSecondCall(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -226,6 +229,7 @@ void extractToCacheReturnsCachedFileOnSecondCall(@TempDir Path tempDir) throws E @Test void changedNativeVersionDoesNotReuseCachedArtifactsForSameSdkVersion(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader firstLoader = classLoaderWithNativeArtifacts(tempDir.resolve("native-v1"), TEST_CLASSIFIER, "1.0.0", FAKE_BINARY_CONTENT, FAKE_CLI_CONTENT); @@ -246,6 +250,7 @@ void changedNativeVersionDoesNotReuseCachedArtifactsForSameSdkVersion(@TempDir P @Test void extractToCacheThrowsWhenClasspathResourceMissing(@TempDir Path tempDir) { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); @@ -255,6 +260,7 @@ void extractToCacheThrowsWhenClasspathResourceMissing(@TempDir Path tempDir) { @Test void extractToCacheThrowsWhenNativeMetadataMissing(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path resourceDir = tempDir.resolve("native").resolve(TEST_CLASSIFIER); Files.createDirectories(resourceDir); Files.write(resourceDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME), FAKE_BINARY_CONTENT); @@ -268,6 +274,7 @@ void extractToCacheThrowsWhenNativeMetadataMissing(@TempDir Path tempDir) throws @Test void extractedBinaryContentsMatchClasspathResource(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -279,6 +286,7 @@ void extractedBinaryContentsMatchClasspathResource(@TempDir Path tempDir) throws @Test void extractToCacheFiltersClasspathByClassifier(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); writeRuntimeResource(tempDir, TEST_CLASSIFIER, FAKE_BINARY_CONTENT); writeRuntimeResource(tempDir, OTHER_CLASSIFIER, OTHER_BINARY_CONTENT); @@ -292,6 +300,7 @@ void extractToCacheFiltersClasspathByClassifier(@TempDir Path tempDir) throws Ex @Test void extractToCacheRepairsInvalidCacheEntry(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); Path cached = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER) .resolve(NativeRuntimeLoader.RUNTIME_FILENAME); @@ -307,6 +316,7 @@ void extractToCacheRepairsInvalidCacheEntry(@TempDir Path tempDir) throws Except @Test void nonExecutableCachedCliIsNotAcceptedAsValid(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); assumeTrue(Files.getFileStore(tempDir).supportsFileAttributeView("posix")); Path cacheBase = tempDir.resolve("cache"); Path cacheDir = cacheBase.resolve(TEST_VERSION).resolve(TEST_NATIVE_VERSION).resolve(TEST_CLASSIFIER); @@ -327,6 +337,7 @@ void nonExecutableCachedCliIsNotAcceptedAsValid(@TempDir Path tempDir) throws Ex @Test void bundledCliSiblingIsUsedWhenClasspathResourceAbsent(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path bundledCliDir = tempDir.resolve("bundled-cli"); Files.createDirectories(bundledCliDir); Path runtimeNode = bundledCliDir.resolve(NativeRuntimeLoader.RUNTIME_FILENAME); @@ -344,6 +355,7 @@ void bundledCliSiblingIsUsedWhenClasspathResourceAbsent(@TempDir Path tempDir) t @Test void classpathResourceWinsOverBundledCliSibling(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); // Source 3: bundled CLI dir with runtime.node (should NOT win) Path bundledCliDir = tempDir.resolve("bundled-cli"); Files.createDirectories(bundledCliDir); @@ -365,6 +377,7 @@ void classpathResourceWinsOverBundledCliSibling(@TempDir Path tempDir) throws Ex @Test void bundledCliSiblingIsIgnoredWhenRuntimeNodeMissing(@TempDir Path tempDir) { + assumeLinuxX64(); Path bundledCliDir = tempDir.resolve("bundled-cli-no-runtime"); // bundledCliDir doesn't even exist — no runtime.node present @@ -396,6 +409,7 @@ void defaultPublisherMovesSourceToTarget(@TempDir Path tempDir) throws Exception @Test void cliIsExecutableBeforeAtomicPublication(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); assumeTrue(Files.getFileStore(tempDir).supportsFileAttributeView("posix")); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeAndCliResources(tempDir, TEST_CLASSIFIER); @@ -411,6 +425,7 @@ void cliIsExecutableBeforeAtomicPublication(@TempDir Path tempDir) throws Except @Test void extractionCleansUpTempFileWhenPublicationFails(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -431,6 +446,7 @@ void extractionCleansUpTempFileWhenPublicationFails(@TempDir Path tempDir) throw @Test void extractionCleansUpTempFileWhenPublisherThrowsIllegalStateException(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -458,6 +474,7 @@ void extractionCleansUpTempFileWhenPublisherThrowsIllegalStateException(@TempDir @Test void concurrentExtractionByMultipleThreadsBothSucceed(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); int threadCount = 8; @@ -495,6 +512,7 @@ void concurrentExtractionByMultipleThreadsBothSucceed(@TempDir Path tempDir) thr @Test void resolveWithNullCliEnvExtractsFromClasspath(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader loader = classLoaderWithRuntimeResource(tempDir, TEST_CLASSIFIER); @@ -507,6 +525,7 @@ void resolveWithNullCliEnvExtractsFromClasspath(@TempDir Path tempDir) throws Ex @Test void resolveThrowsWhenNoSourceIsAvailable(@TempDir Path tempDir) { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); @@ -517,6 +536,7 @@ void resolveThrowsWhenNoSourceIsAvailable(@TempDir Path tempDir) { @Test void resolveFallsBackToRuntimeAlongsideBundledCli(@TempDir Path tempDir) throws Exception { + assumeLinuxX64(); Path cacheBase = tempDir.resolve("cache"); ClassLoader emptyLoader = new URLClassLoader(new URL[0], null); Path bundledCli = tempDir.resolve("copilot"); @@ -534,6 +554,17 @@ void resolveFallsBackToRuntimeAlongsideBundledCli(@TempDir Path tempDir) throws // Helpers // ------------------------------------------------------------------------- + private static void assumeLinuxX64() { + String actualClassifier; + try { + actualClassifier = PlatformDetector.detectClassifier(); + } catch (IllegalStateException ex) { + actualClassifier = "unsupported"; + } + assumeTrue(TEST_CLASSIFIER.equals(actualClassifier), + "Requires linux-x64; detected " + actualClassifier + "; see #2323"); + } + private static ClassLoader classLoaderWithVersionResource(Path tempDir, String version) throws IOException { Path propsFile = tempDir.resolve(NativeRuntimeLoader.VERSION_RESOURCE); Files.writeString(propsFile, "version=" + version + "\n"); From 9ce1577c94cfbe06fcc0e58bfcd22c2e00b48f91 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Aug 2026 00:14:36 +0000 Subject: [PATCH 58/72] docs: update version references to 1.0.11-preview.2 --- java/README.md | 8 ++++---- java/sdk/jbang-example.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/README.md b/java/README.md index ea72b72b1..e64e22d81 100644 --- a/java/README.md +++ b/java/README.md @@ -32,14 +32,14 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. com.github copilot-sdk-java - 1.0.10-preview.0 + 1.0.11-preview.2 ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.10-preview.0' +implementation 'com.github:copilot-sdk-java:1.0.11-preview.2' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.11-preview.0-SNAPSHOT + 1.0.12-preview.2-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.11-preview.0-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.12-preview.2-SNAPSHOT' ``` ## In-process mode (experimental) diff --git a/java/sdk/jbang-example.java b/java/sdk/jbang-example.java index 39d64ad4a..e60509eed 100644 --- a/java/sdk/jbang-example.java +++ b/java/sdk/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.10-preview.0 +//DEPS com.github:copilot-sdk-java:1.0.11-preview.2 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From 89b68c1ad5c78e6f5ec36a6df61316eafe5116f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Aug 2026 00:15:21 +0000 Subject: [PATCH 59/72] [maven-release-plugin] prepare release java/v1.0.11-preview.2 --- java/copilot-native/pom.xml | 4 ++-- java/pom.xml | 4 ++-- java/sdk/pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index a6e3869b8..d6748b3b2 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.0-SNAPSHOT + 1.0.11-preview.2 ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11-preview.2 diff --git a/java/pom.xml b/java/pom.xml index 1fcbcca3f..33beb7b6a 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.0-SNAPSHOT + 1.0.11-preview.2 pom GitHub Copilot SDK :: Java :: Parent @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11-preview.2 diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index 374ca06b7..584eecd9b 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.0-SNAPSHOT + 1.0.11-preview.2 ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11-preview.2 From 811adc050a82d823cc6f6891576f30058554af8d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 13 Aug 2026 00:15:25 +0000 Subject: [PATCH 60/72] [maven-release-plugin] prepare for next development iteration --- java/copilot-native/pom.xml | 4 ++-- java/pom.xml | 4 ++-- java/sdk/pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index d6748b3b2..ef4ec02ab 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.2 + 1.0.12-preview.2-SNAPSHOT ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11-preview.2 + HEAD diff --git a/java/pom.xml b/java/pom.xml index 33beb7b6a..cb6662458 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.2 + 1.0.12-preview.2-SNAPSHOT pom GitHub Copilot SDK :: Java :: Parent @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11-preview.2 + HEAD diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index 584eecd9b..0c93a2b30 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11-preview.2 + 1.0.12-preview.2-SNAPSHOT ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11-preview.2 + HEAD From f0c89d176399b24d68d65814009317644d345fca Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 13 Aug 2026 17:04:02 -0400 Subject: [PATCH 61/72] Fix codegen for internal runtime schemas (#2331) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f0193925-3c4a-4142-b5c0-4ea48168a24b --- scripts/codegen/csharp.ts | 13 +++++++------ scripts/codegen/typescript.ts | 29 ++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index 4fb71cd0c..46f50daf5 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -40,6 +40,7 @@ import { isSchemaExperimental, isSchemaInternal, isOpaqueJson, + isOpaqueInProcess, isObjectSchema, isVoidSchema, getNullableInner, @@ -355,10 +356,10 @@ function failUnmappable(context: string, schema: JSONSchema7): never { ); } -function omitUntypedInternalProperties(value: unknown): void { +function omitUnrepresentableInternalProperties(value: unknown): void { if (!value || typeof value !== "object") return; if (Array.isArray(value)) { - value.forEach(omitUntypedInternalProperties); + value.forEach(omitUnrepresentableInternalProperties); return; } @@ -377,16 +378,16 @@ function omitUntypedInternalProperties(value: unknown): void { schema.enum !== undefined || schema.const !== undefined || isOpaqueJson(schema); - if (isSchemaInternal(schema) && !hasType) { + if (isSchemaInternal(schema) && (!hasType || isOpaqueInProcess(schema))) { delete (properties as Record)[name]; } else { - omitUntypedInternalProperties(property); + omitUnrepresentableInternalProperties(property); } } } for (const [name, child] of Object.entries(node)) { - if (name !== "properties") omitUntypedInternalProperties(child); + if (name !== "properties") omitUnrepresentableInternalProperties(child); } } @@ -2604,7 +2605,7 @@ function generateRpcCode( externalValueTypes: Set = new Set() ): string { schema = cloneSchemaForCodegen(schema); - omitUntypedInternalProperties(schema); + omitUnrepresentableInternalProperties(schema); emittedRpcClassSchemas.clear(); emittedRpcEnumResultTypes.clear(); experimentalRpcTypes.clear(); diff --git a/scripts/codegen/typescript.ts b/scripts/codegen/typescript.ts index 0c6866ed6..4984816d8 100644 --- a/scripts/codegen/typescript.ts +++ b/scripts/codegen/typescript.ts @@ -383,16 +383,39 @@ export function normalizeSchemaForTypeScript( root.definitions = definitions; delete root.$defs; - const rewrite = (value: unknown): unknown => { + const internalDefinitionNames = new Set( + Object.entries(definitions) + .filter(([, definition]) => typeof definition === "object" && definition !== null && isSchemaInternal(definition as JSONSchema7)) + .map(([name]) => name) + ); + const isInternalUnionVariant = (value: unknown): boolean => { + if (!value || typeof value !== "object") return false; + const variant = value as JSONSchema7; + if (isSchemaInternal(variant)) return true; + const match = variant.$ref?.match(/^#\/(?:definitions|\$defs)\/([^/]+)$/); + return match ? internalDefinitionNames.has(match[1]) : false; + }; + + const rewrite = (value: unknown, withinInternalDefinition = false): unknown => { if (Array.isArray(value)) { - return value.map(rewrite); + return value.map((item) => rewrite(item, withinInternalDefinition)); } if (!value || typeof value !== "object") { return value; } + const source = value as Record; + const isInternal = withinInternalDefinition || isSchemaInternal(source as JSONSchema7); const rewritten = Object.fromEntries( - Object.entries(value as Record).map(([key, child]) => [key, rewrite(child)]) + Object.entries(source).map(([key, child]) => { + const publicChild = + !isInternal && + (key === "anyOf" || key === "oneOf") && + Array.isArray(child) + ? child.filter((variant) => !isInternalUnionVariant(variant)) + : child; + return [key, rewrite(publicChild, isInternal)]; + }) ) as Record; if (isBareSchemaNode(rewritten as JSONSchema7)) { From 3b0d55633c4821a45357616baba6b7f4b51ccada Mon Sep 17 00:00:00 2001 From: Matthew Rayermann Date: Thu, 13 Aug 2026 21:21:31 +0000 Subject: [PATCH 62/72] [SDK/Factories] Add argsSchema To The Factory Authoring Surface (#2315) * [SDK/Factories] Add argsSchema To The Factory Authoring Surface FactoryMeta now declares an optional argsSchema, typed as the existing FactoryJsonSchema. The field already crossed the wire because defineFactory snapshots meta whole, so this is additive and type-level: it makes a runtime feature discoverable to extension authors writing against the published types. Without a declared schema nothing validates a caller's args. A malformed call starts a run, takes a user approval, spends credits, and then fails inside the factory body. With one, the CLI rejects it before the run row exists and the model retries against a correction hint. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Scope argsSchema docs to the run_factory path and widen the E2E fixture Enforcement lives behind toolRunFactoryValidateArgs, which the runtime calls only from runFactoryTool. session.factory.run does not validate, so the docs and JSDoc now say which caller is checked instead of implying all of them are. The argument-echo fixture declared ["object","null"] while its contract is to echo any JsonValue, and it is invoked with an array. Nothing broke, because the SDK path does not validate, but the narrow declaration was dishonest and would have become load-bearing if that path ever gained validation. Widened it to the factory's real contract and corrected the comment, which claimed the fixture exercised enforcement when it only exercises registration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- nodejs/docs/factories.md | 23 ++++- nodejs/src/factory.ts | 12 ++- nodejs/src/types.ts | 26 ++++- .../test/e2e/fixtures/factory-extension.mjs | 9 ++ nodejs/test/factory.test.ts | 99 +++++++++++++++++++ 5 files changed, 161 insertions(+), 8 deletions(-) diff --git a/nodejs/docs/factories.md b/nodejs/docs/factories.md index a22767905..e6d9ce2bc 100644 --- a/nodejs/docs/factories.md +++ b/nodejs/docs/factories.md @@ -16,6 +16,13 @@ const reviewChanged = defineFactory({ "Review changed files and verify the findings. " + "args: { files: string[] } — the paths to review.", phases: [{ title: "Review" }, { title: "Verify" }], + argsSchema: { + type: "object", + required: ["files"], + properties: { + files: { type: "array", items: { type: "string" } }, + }, + }, limits: { maxConcurrentSubagents: 3, maxTotalSubagents: 10, @@ -41,9 +48,19 @@ const reviewChanged = defineFactory({ const session = await joinSession({ factories: [reviewChanged] }); ``` -Factory metadata contains a stable `name`, a human-readable `description`, declared `phases`, and optional `limits`. Phase entries contain a `title` and optional `detail`. +Factory metadata contains a stable `name`, a human-readable `description`, declared `phases`, an optional `argsSchema`, and optional `limits`. Phase entries contain a `title` and optional `detail`. + +## Declaring an argument shape + +A factory that reads `ctx.args` should declare `meta.argsSchema`, as the example above does. When the model invokes the factory through the `run_factory` tool, the CLI validates `args` against the declaration **before** the run starts. + +Declaring one turns an expensive failure into a cheap one. With a schema, a malformed call is rejected up front — the model gets a correction hint and retries, and no run row, permission prompt, or credit spend happens. Without one, nothing validates: the run starts, takes a user approval, spends credits, and then dies inside the factory body with a confusing error. Agents can read the declared shape with `factories_manage` using `operation: "inspect"`. + +Enforcement covers structure — types, required properties, and enum or const values. Finer constraints such as `minLength`, `pattern`, or `additionalProperties` are recorded in the declaration but not enforced. The accepted vocabulary is the `FactoryJsonSchema` subset also used for subagent structured output: `type`, `required`, `enum`, `const`, recursive `properties`/`items`, and `anyOf`/`oneOf`/`allOf`. A `type` is one of `null`, `boolean`, `integer`, `number`, `string`, `array`, or `object`, or a non-empty array of those such as `["object", "null"]`. A declaration outside that subset is rejected at registration. + +`argsSchema` is optional and backward compatible. A factory that omits it behaves exactly as before, so **the `description` is then the only thing telling an agent what arguments to supply** — state the expected shape there. -There is no declared schema for `ctx.args`. The `run_factory` tool forwards `args` verbatim and its parameter is untyped, so **the `description` is the only thing telling an agent what arguments to supply** — state the expected shape there whenever a factory reads `ctx.args`, as the example above does. Arguments supplied by an extension calling `session.factory.run(...)` directly are typed through `defineFactory`, but that typing does not reach the model. A factory that reads `ctx.args` should validate it rather than assume a shape. +Validation covers the model's `run_factory` path only. An extension calling `session.factory.run(...)` directly is not validated against `argsSchema`; those arguments are typed through `defineFactory` instead, and that typing does not reach the model. So a factory that reads `ctx.args` should still validate it rather than assume a shape — the declared subset does not enforce every constraint, and it does not run at all on the SDK path. `defineFactory` accepts a `run(context)` function returning `Promise`, where `TResult` is `JsonValue | void`. Objects, arrays, strings, numbers, booleans, and `null` are valid results. Returning `undefined` completes the factory with no result. Other non-JSON values are rejected. @@ -193,7 +210,7 @@ async ({ args, agent, phase }) => { }; ``` -Authoring registers the factory but does not run it. Invoke it afterwards with `run_factory`. Use `factories_manage` with `operation: "list"` to see the factories already registered in the session and `operation: "inspect"` to read one factory's description, phases, and limits before running it. +Authoring registers the factory but does not run it. Invoke it afterwards with `run_factory`. Use `factories_manage` with `operation: "list"` to see the factories already registered in the session and `operation: "inspect"` to read one factory's description, phases, declared argument shape, and limits before running it. ## Observe a run diff --git a/nodejs/src/factory.ts b/nodejs/src/factory.ts index 8ad1c7acb..8a6c78747 100644 --- a/nodejs/src/factory.ts +++ b/nodejs/src/factory.ts @@ -62,12 +62,16 @@ export type JsonValue = | { [key: string]: JsonValue }; /** - * Conservative JSON shape language accepted for structured factory agent output. + * Conservative JSON shape language accepted by the Agent Factories surface, for + * both structured factory agent output and a factory's declared `argsSchema`. * - * This is a best-effort structural guard used to decide whether a subagent's - * structured output should be accepted or retried — **not** a full JSON Schema + * This is a best-effort structural guard — used to decide whether a subagent's + * structured output should be accepted or retried, and whether a caller's + * factory `args` match the declared shape — **not** a full JSON Schema * validator. Only these keywords are honored: `type`, `required`, `enum`, - * `const`, recursive `properties`/`items`, and `anyOf`/`oneOf`/`allOf`. + * `const`, recursive `properties`/`items`, and `anyOf`/`oneOf`/`allOf`. A `type` + * is one of `null`, `boolean`, `integer`, `number`, `string`, `array`, or + * `object`, or a non-empty array of those (for example `["object", "null"]`). * * Everything else is **ignored, not enforced**. In particular, string * constraints (`pattern`, `minLength`, `maxLength`, `format`), numeric ranges diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 2567d7d31..167e52d87 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -19,7 +19,7 @@ import type { SessionEvent as GeneratedSessionEvent, } from "./generated/session-events.js"; import type { CopilotSession } from "./session.js"; -import type { JsonValue } from "./factory.js"; +import type { FactoryJsonSchema, JsonValue } from "./factory.js"; import type { GitHubTelemetryNotification, ModelBillingTokenPrices, @@ -2007,6 +2007,30 @@ export interface FactoryMeta { description: string; /** Display metadata for the progress phases the factory may report. */ phases: Array<{ title: string; detail?: string }>; + /** + * Optional declared shape of the arguments this factory expects as `ctx.args`. + * + * Declaring one is strongly recommended for any factory that reads `ctx.args`. + * When the model invokes the factory through the `run_factory` tool, the CLI + * validates `args` against this declaration **before** the run starts, so a + * malformed call is rejected with a correction hint and retried without ever + * creating a run row, prompting the user for permission, or spending credits. A + * factory that declares nothing is never validated: a malformed call starts, + * takes an approval, spends credits, and then fails inside the factory body. + * `factories_manage` with `operation: "inspect"` reports the declared shape so an + * agent can read it before invoking. + * + * This covers the model's `run_factory` path only. `session.factory.run(...)` is + * not validated against the declaration, so a factory should still check + * `ctx.args` rather than assume the declared shape held. + * + * Enforcement covers structure — types, required properties, and enum/const + * values. Finer constraints such as `minLength`, `pattern`, and + * `additionalProperties` are recorded in the declaration but not enforced. See + * {@link FactoryJsonSchema} for the accepted subset. A declaration outside that + * subset is rejected at registration. + */ + argsSchema?: FactoryJsonSchema; /** Optional resource ceilings presented to the user before execution. */ limits?: FactoryLimits; } diff --git a/nodejs/test/e2e/fixtures/factory-extension.mjs b/nodejs/test/e2e/fixtures/factory-extension.mjs index 5f6c19e2b..45227a1be 100644 --- a/nodejs/test/e2e/fixtures/factory-extension.mjs +++ b/nodejs/test/e2e/fixtures/factory-extension.mjs @@ -18,6 +18,15 @@ const argumentEcho = defineFactory({ name: "argument-echo", description: "Return the invocation arguments verbatim.", phases: [], + // Proves a declared shape survives the SDK boundary and registers against a + // real runtime. It does not exercise enforcement: `argsSchema` is checked by + // the model's `run_factory` tool, and these tests invoke `session.factory.run`, + // which does not validate. The declaration stays as wide as this factory's + // actual contract — it echoes any JsonValue, and is called with an array, an + // object, and nothing — so it cannot constrain the runs below. + argsSchema: { + type: ["object", "array", "string", "number", "integer", "boolean", "null"], + }, }, run: async ({ args }) => args, }); diff --git a/nodejs/test/factory.test.ts b/nodejs/test/factory.test.ts index f550c3bc9..3d85b972d 100644 --- a/nodejs/test/factory.test.ts +++ b/nodejs/test/factory.test.ts @@ -15,6 +15,7 @@ import { type FactoryAgentOptions, type FactoryContext, type FactoryDefinition, + type FactoryJsonSchema, type JsonValue, } from "../src/factory.js"; @@ -461,6 +462,104 @@ describe("factories", () => { ); }); + it("carries a declared argsSchema through defineFactory into the registration payload", async () => { + const client = new CopilotClient(); + await client.start(); + onTestFinished(() => stopClient(client)); + + const argsSchema = { + type: "object", + required: ["repoPath"], + properties: { + repoPath: { type: "string" }, + depth: { type: ["integer", "null"] }, + mode: { enum: ["fast", "thorough"] }, + }, + } satisfies FactoryJsonSchema; + const meta = { + name: "declares-args", + description: "Declares the argument shape it expects", + phases: [], + argsSchema, + }; + const factory = defineFactory({ meta, run: async () => ({ ok: true }) }); + + // The declaration is snapshotted and deep-frozen like the rest of the + // metadata, so it cannot be mutated after registration. + expect(factory.meta.argsSchema).toEqual(argsSchema); + expect(factory.meta.argsSchema).not.toBe(argsSchema); + expect(Object.isFrozen(factory.meta.argsSchema)).toBe(true); + expect(() => { + // @ts-expect-error handle.meta.argsSchema is deeply readonly. + factory.meta.argsSchema!.type = "array"; + }).toThrow(TypeError); + + const omitted = defineFactory({ + meta: { name: "omits-args", description: "Declares nothing", phases: [] }, + run: async () => ({ ok: true }), + }); + expect(omitted.meta.argsSchema).toBeUndefined(); + expect("argsSchema" in omitted.meta).toBe(false); + + const sendRequest = vi + .spyOn( + (client as never as { connection: { sendRequest: Function } }).connection, + "sendRequest" + ) + .mockImplementation(async (method: string, params: Record) => { + if (method === "session.resume") { + return { sessionId: params.sessionId }; + } + throw new Error(`Unexpected method: ${method}`); + }); + + await client.resumeSessionForExtension( + "session-args-schema", + { onPermissionRequest: () => ({ kind: "approved" }) }, + [factory, omitted] + ); + + const payload = sendRequest.mock.calls.find( + ([method]) => method === "session.resume" + )![1] as { factories: Array> }; + // The schema has to survive JSON serialization to reach the runtime, which + // validates `args` against it before a run row exists. + expect(JSON.parse(JSON.stringify(payload.factories))[0].argsSchema).toEqual(argsSchema); + expect(payload.factories[1]).not.toHaveProperty("argsSchema"); + }); + + it("documents argsSchema consistently with the runtime's enforced subset", () => { + const publicTypes = readFileSync(new URL("../src/types.ts", import.meta.url), "utf8"); + const publicApi = readFileSync(new URL("../src/factory.ts", import.meta.url), "utf8"); + const guide = readFileSync(new URL("../docs/factories.md", import.meta.url), "utf8"); + const normalizeJSDoc = (document: string) => + document.replace(/\r?\n\s*\* ?/g, " ").replace(/\s+/g, " "); + + expect(publicTypes).toContain("argsSchema?: FactoryJsonSchema;"); + + // The `run_factory` tool tells the model exactly this. The two surfaces + // have to agree about what a declaration does and does not enforce. + for (const document of [normalizeJSDoc(publicTypes), guide]) { + expect(document).toContain("types, required properties, and enum"); + expect(document).toMatch( + /`minLength`, `pattern`,? (?:and|or) `additionalProperties` are recorded/ + ); + } + expect(normalizeJSDoc(publicTypes)).toContain("before** the run starts"); + // Enforcement is tool-path only: `toolRunFactoryValidateArgs` is called from + // the runtime's runFactoryTool, and never from `session.factory.run`. Both + // surfaces must keep saying so, or authors will assume their own SDK-initiated + // runs are checked. + expect(normalizeJSDoc(publicTypes)).toContain( + "`session.factory.run(...)` is not validated against the declaration" + ); + expect(guide).toContain("Validation covers the model's `run_factory` path only"); + expect(normalizeJSDoc(publicApi)).toContain( + "`null`, `boolean`, `integer`, `number`, `string`, `array`, or `object`" + ); + expect(guide).toContain("no run row, permission prompt, or credit spend happens"); + }); + it("serializes only factory metadata in the extension resume payload", async () => { const client = new CopilotClient(); await client.start(); From 1935fd3029f86f2b9f077dffc4cce1d2807886a1 Mon Sep 17 00:00:00 2001 From: Lutz Roeder Date: Fri, 14 Aug 2026 00:56:04 +0000 Subject: [PATCH 63/72] Add built-in plugin directory support (#2330) * Add built-in plugin directory support * Address built-in plugin directory feedback Use a plain code fence for the single-language docs example and clear Go connection state when built-in plugin registration fails so reconnect can start cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd609bcf-9f69-4d40-960b-dbd12e90158a * Address .NET code scanning comments Make the built-in plugin path validation filter explicit and avoid Path.Combine in the test paths flagged by code scanning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cd609bcf-9f69-4d40-960b-dbd12e90158a --------- Copilot-Session: cd609bcf-9f69-4d40-960b-dbd12e90158a --- docs/features/plugin-directories.md | 35 ++++ dotnet/src/Client.cs | 40 ++++ dotnet/src/Types.cs | 8 + dotnet/test/Unit/CloneTests.cs | 3 + dotnet/test/Unit/GitHubTelemetryTests.cs | 66 ++++++ go/client.go | 22 ++ go/client_test.go | 194 ++++++++++++++++++ go/types.go | 5 + .../com/github/copilot/CopilotClient.java | 28 ++- .../copilot/rpc/CopilotClientOptions.java | 44 +++- .../copilot/BuiltinPluginDirectoriesTest.java | 128 ++++++++++++ nodejs/src/client.ts | 24 ++- nodejs/src/types.ts | 7 + nodejs/test/client.test.ts | 47 ++++- python/copilot/client.py | 22 ++ python/test_client.py | 45 ++++ rust/src/lib.rs | 55 +++++ rust/tests/builtin_plugin_directories_test.rs | 135 ++++++++++++ 18 files changed, 898 insertions(+), 10 deletions(-) create mode 100644 java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java create mode 100644 rust/tests/builtin_plugin_directories_test.rs diff --git a/docs/features/plugin-directories.md b/docs/features/plugin-directories.md index ccd95df95..4af11d9a7 100644 --- a/docs/features/plugin-directories.md +++ b/docs/features/plugin-directories.md @@ -240,6 +240,41 @@ let client = Client::start( > The example above uses an stdio runtime connection — the default when the SDK bundles the CLI. If you connect to an external runtime via a URL (`forUri` / `ForUri`), pass `--plugin-dir` to the long-running CLI server when you start it; the SDK does not forward `--plugin-dir` to runtimes it didn't spawn. +## Trusted host-bundled plugin directories + +Applications that ship their own trusted plugins can register them as a client startup option. The SDK sends the complete ordered set after connecting and verifying the protocol, before `start` returns or any session can be created. Paths must be absolute; leaving the option unset or empty makes no RPC call. + + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +async function main() { + const client = new CopilotClient({ + builtinPluginDirectories: [ + "/opt/my-app/copilot-plugins/core", + "/opt/my-app/copilot-plugins/github", + ], + }); + await client.start(); +} + +main(); +``` + + +The equivalent option in each SDK is: + +| SDK | Startup option | +|---|---| +| Node.js / TypeScript | `builtinPluginDirectories: string[]` | +| Python | `builtin_plugin_directories=[...]` | +| Go | `BuiltinPluginDirectories: []string{...}` | +| .NET | `BuiltinPluginDirectories = [...]` | +| Java | `.setBuiltinPluginDirectories(List.of(Path.of(...)))` | +| Rust | `.with_builtin_plugin_directories([...])` | + +This is a trust boundary for plugins bundled and controlled by the host application. It is distinct from `--plugin-dir`, which is a CLI process launch argument for explicitly loading ordinary plugin directories. The startup option also works when connecting to an existing runtime because it is sent over JSON-RPC rather than forwarded as a process argument. + ## What a plugin can contribute Loading a plugin directory makes its extensions visible to every session created by the client. The runtime merges plugin-provided extensions with anything you register inline: diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 8116be28e..58c1074c0 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -76,6 +76,7 @@ public sealed partial class CopilotClient : IDisposable, IAsyncDisposable private readonly ILogger _logger; private readonly int? _optionsPort; private readonly string? _optionsHost; + private readonly string[] _builtinPluginDirectories; private readonly Func>>? _onListModels; private readonly List _lifecycleHandlers = []; @@ -138,6 +139,14 @@ public CopilotClient(CopilotClientOptions? options = null) { _options = options ?? new(); _connection = _options.Connection ?? ResolveDefaultConnection(_options); + _builtinPluginDirectories = _options.BuiltinPluginDirectories?.ToArray() ?? []; + foreach (var path in _builtinPluginDirectories.Where(path => !IsFullyQualifiedPath(path))) + { + throw new ArgumentException( + $"{nameof(CopilotClientOptions)}.{nameof(CopilotClientOptions.BuiltinPluginDirectories)} " + + $"must contain only absolute paths: {path}", + nameof(options)); + } switch (_connection) { @@ -317,6 +326,26 @@ private static Uri ParseRuntimeUrl(string url) return new Uri(url); } + private static bool IsFullyQualifiedPath(string path) + { + if (string.IsNullOrEmpty(path) || !Path.IsPathRooted(path)) + { + return false; + } +#if NETSTANDARD2_0 + if (Path.DirectorySeparatorChar != '\\') + { + return true; + } + + bool IsSeparator(char value) => value == '\\' || value == '/'; + return (path.Length >= 3 && path[1] == ':' && IsSeparator(path[2])) + || (path.Length >= 2 && IsSeparator(path[0]) && IsSeparator(path[1])); +#else + return Path.IsPathFullyQualified(path); +#endif + } + ///

/// Starts the Copilot client and connects to the server. /// @@ -423,6 +452,13 @@ async Task StartCoreAsync(CancellationToken ct) "CopilotClient.StartAsync protocol verification complete. Elapsed={Elapsed}", startTimestamp); + if (_builtinPluginDirectories.Length > 0) + { + var request = new BuiltinPluginDirectoriesRequest(_builtinPluginDirectories); + await InvokeRpcAsync( + connection.Rpc, "plugins.builtin.set", [request], null, ct); + } + var sessionFsTimestamp = Stopwatch.GetTimestamp(); await ConfigureSessionFsAsync(ct); if (_options.SessionFs is not null) @@ -2946,6 +2982,9 @@ internal record ConnectHandshakeRequest( string? Token, [property: JsonPropertyName("enableGitHubTelemetryForwarding")] bool? EnableGitHubTelemetryForwarding = null); + internal record BuiltinPluginDirectoriesRequest( + string[] Paths); + internal record SetForegroundSessionRequest( string SessionId); @@ -2981,6 +3020,7 @@ internal record HooksInvokeResponse( [JsonSerializable(typeof(GetSessionMetadataRequest))] [JsonSerializable(typeof(GetSessionMetadataResponse))] [JsonSerializable(typeof(ConnectHandshakeRequest))] + [JsonSerializable(typeof(BuiltinPluginDirectoriesRequest))] [JsonSerializable(typeof(McpOAuthTokenStorageMode))] [JsonSerializable(typeof(EmbeddingCacheStorageMode))] [JsonSerializable(typeof(ModelCapabilitiesOverride))] diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index 59f892140..c0810b387 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -309,6 +309,7 @@ private CopilotClientOptions(CopilotClientOptions? other) Connection = other.Connection; WorkingDirectory = other.WorkingDirectory; BaseDirectory = other.BaseDirectory; + BuiltinPluginDirectories = other.BuiltinPluginDirectories is null ? null : [.. other.BuiltinPluginDirectories]; Environment = other.Environment; GitHubToken = other.GitHubToken; Logger = other.Logger; @@ -358,6 +359,13 @@ private CopilotClientOptions(CopilotClientOptions? other) ///
public string? BaseDirectory { get; set; } + /// + /// Absolute paths to trusted plugin directories bundled by the host. + /// When non-empty, the complete set is registered with the runtime during + /// startup before sessions can be created. + /// + public IList? BuiltinPluginDirectories { get; set; } + /// /// Log level for the Copilot runtime. Use the well-known values on /// (, diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index 844eca135..4bacdfe33 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -20,6 +20,7 @@ public void CopilotClientOptions_Clone_CopiesAllProperties() GitHubToken = "ghp_test", UseLoggedInUser = false, BaseDirectory = "/custom/copilot/home", + BuiltinPluginDirectories = ["/plugins/core", "/plugins/github"], EnableRemoteSessions = true, SessionIdleTimeoutSeconds = 600, }; @@ -33,6 +34,8 @@ public void CopilotClientOptions_Clone_CopiesAllProperties() Assert.Equal(original.GitHubToken, clone.GitHubToken); Assert.Equal(original.UseLoggedInUser, clone.UseLoggedInUser); Assert.Equal(original.BaseDirectory, clone.BaseDirectory); + Assert.Equal(original.BuiltinPluginDirectories, clone.BuiltinPluginDirectories); + Assert.NotSame(original.BuiltinPluginDirectories, clone.BuiltinPluginDirectories); Assert.Equal(original.EnableRemoteSessions, clone.EnableRemoteSessions); Assert.Equal(original.SessionIdleTimeoutSeconds, clone.SessionIdleTimeoutSeconds); } diff --git a/dotnet/test/Unit/GitHubTelemetryTests.cs b/dotnet/test/Unit/GitHubTelemetryTests.cs index 24e633387..a4a241e38 100644 --- a/dotnet/test/Unit/GitHubTelemetryTests.cs +++ b/dotnet/test/Unit/GitHubTelemetryTests.cs @@ -17,6 +17,60 @@ namespace GitHub.Copilot.Test.Unit; public sealed class GitHubTelemetryTests { + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task BuiltinPluginDirectories_Default_Or_Empty_Does_Not_Call_Rpc(bool useEmpty) + { + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + BuiltinPluginDirectories = useEmpty ? [] : null, + }); + + await client.StartAsync(); + + Assert.Equal(0, server.BuiltinPluginSetCount); + } + + [Fact] + public async Task BuiltinPluginDirectories_Are_Set_Once_Before_Start_Completes() + { + var paths = new[] + { + Path.GetFullPath(Path.Join("plugins", "core")), + Path.GetFullPath(Path.Join("plugins", "github")), + }; + await using var server = await FakeTelemetryServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + BuiltinPluginDirectories = paths, + }); + + await client.StartAsync(); + + Assert.Equal(1, server.BuiltinPluginSetCount); + var payload = server.LastBuiltinPluginParams + ?? throw new InvalidOperationException("plugins.builtin.set was not captured."); + Assert.Collection( + payload.GetProperty("paths").EnumerateArray(), + value => Assert.Equal(paths[0], value.GetString()), + value => Assert.Equal(paths[1], value.GetString())); + } + + [Fact] + public void BuiltinPluginDirectories_Reject_Relative_Paths() + { + var exception = Assert.Throws(() => new CopilotClient(new CopilotClientOptions + { + BuiltinPluginDirectories = ["plugins/core"], + })); + + Assert.Contains("absolute paths", exception.Message); + } + [Fact] public async Task CreateSession_Opts_Into_Forwarding_When_Handler_Provided() { @@ -266,6 +320,10 @@ public string Url public JsonElement? LastConnectParams { get; private set; } + public JsonElement? LastBuiltinPluginParams { get; private set; } + + public int BuiltinPluginSetCount { get; private set; } + public static Task StartAsync() { var listener = new TcpListener(IPAddress.Loopback, 0); @@ -347,6 +405,7 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel object? result = method switch { "connect" => CaptureConnect(request), + "plugins.builtin.set" => CaptureBuiltinPluginDirectories(request), "session.create" => CaptureCreate(request), "session.resume" => CaptureResume(request), "session.send" => new Dictionary { ["messageId"] = "message-1" }, @@ -375,6 +434,13 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel }; } + private Dictionary CaptureBuiltinPluginDirectories(JsonElement request) + { + BuiltinPluginSetCount++; + LastBuiltinPluginParams = request.TryGetProperty("params", out var p) ? p.Clone() : null; + return new Dictionary(); + } + private Dictionary CaptureCreate(JsonElement request) { LastCreateParams = request.TryGetProperty("params", out var p) ? p.Clone() : null; diff --git a/go/client.go b/go/client.go index 7d9b9e6bf..fb02897f9 100644 --- a/go/client.go +++ b/go/client.go @@ -38,6 +38,7 @@ import ( "net" "os" "os/exec" + "path/filepath" "regexp" "strconv" "strings" @@ -225,6 +226,12 @@ func NewClient(options *ClientOptions) *Client { if options != nil { opts = *options } + for _, path := range opts.BuiltinPluginDirectories { + if !filepath.IsAbs(path) { + panic(fmt.Sprintf("BuiltinPluginDirectories must contain only absolute paths: %s", path)) + } + } + opts.BuiltinPluginDirectories = append([]string(nil), opts.BuiltinPluginDirectories...) // Resolve the connection. An explicit connection always wins; otherwise // honor the same process/environment override as the other SDKs. @@ -453,6 +460,21 @@ func (c *Client) Start(ctx context.Context) error { return errors.Join(err, killErr) } + if len(c.options.BuiltinPluginDirectories) > 0 { + if _, err := c.client.Request(ctx, "plugins.builtin.set", map[string]any{ + "paths": c.options.BuiltinPluginDirectories, + }); err != nil { + c.client.Stop() + c.client = nil + c.conn = nil + c.RPC = nil + c.internalRPC = nil + killErr := c.killProcess() + c.state = stateError + return errors.Join(err, killErr) + } + } + // If a session filesystem provider was configured, register it. if c.options.SessionFS != nil { req := &rpc.SessionFSSetProviderRequest{ diff --git a/go/client_test.go b/go/client_test.go index 8152fb469..f21442679 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -139,6 +139,200 @@ func TestClient_URLParsing(t *testing.T) { }) } +func TestClient_BuiltinPluginDirectories(t *testing.T) { + t.Run("default and empty do not call RPC", func(t *testing.T) { + for _, paths := range [][]string{nil, []string{}} { + t.Run(fmt.Sprintf("len=%d", len(paths)), func(t *testing.T) { + url, requests, cleanup := newStartupRPCServer(t) + defer cleanup() + + client := NewClient(&ClientOptions{ + Connection: URIConnection{URL: url}, + BuiltinPluginDirectories: paths, + }) + if err := client.Start(t.Context()); err != nil { + t.Fatalf("Start failed: %v", err) + } + defer client.ForceStop() + + if got := countMethod(requests(), "plugins.builtin.set"); got != 0 { + t.Fatalf("plugins.builtin.set call count = %d, want 0", got) + } + }) + } + }) + + t.Run("configured paths call RPC once", func(t *testing.T) { + url, requests, cleanup := newStartupRPCServer(t) + defer cleanup() + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd failed: %v", err) + } + paths := []string{ + filepath.Join(cwd, "plugins", "core"), + filepath.Join(cwd, "plugins", "github"), + } + + client := NewClient(&ClientOptions{ + Connection: URIConnection{URL: url}, + BuiltinPluginDirectories: paths, + }) + if err := client.Start(t.Context()); err != nil { + t.Fatalf("Start failed: %v", err) + } + defer client.ForceStop() + + var calls []startupRPCRequest + for _, request := range requests() { + if request.Method == "plugins.builtin.set" { + calls = append(calls, request) + } + } + if len(calls) != 1 { + t.Fatalf("plugins.builtin.set call count = %d, want 1", len(calls)) + } + var payload struct { + Paths []string `json:"paths"` + } + if err := json.Unmarshal(calls[0].Params, &payload); err != nil { + t.Fatalf("decode plugins.builtin.set params: %v", err) + } + if !reflect.DeepEqual(payload.Paths, paths) { + t.Fatalf("paths = %v, want %v", payload.Paths, paths) + } + }) + + t.Run("relative path panics", func(t *testing.T) { + defer func() { + if recovered := recover(); recovered == nil { + t.Fatal("expected NewClient to panic") + } + }() + NewClient(&ClientOptions{BuiltinPluginDirectories: []string{"plugins/core"}}) + }) + + t.Run("startup RPC failure clears transport for reconnect", func(t *testing.T) { + url, _, cleanup := newStartupRPCServerWithBuiltinFailure(t, true) + defer cleanup() + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd failed: %v", err) + } + client := NewClient(&ClientOptions{ + Connection: URIConnection{URL: url}, + BuiltinPluginDirectories: []string{filepath.Join(cwd, "plugins", "core")}, + }) + + if err := client.Start(t.Context()); err == nil { + t.Fatal("Start unexpectedly succeeded") + } + if client.client != nil { + t.Fatal("client transport was not cleared after startup RPC failure") + } + if client.conn != nil { + t.Fatal("connection was not cleared after startup RPC failure") + } + if client.RPC != nil { + t.Fatal("typed RPC client was not cleared after startup RPC failure") + } + if client.internalRPC != nil { + t.Fatal("internal RPC client was not cleared after startup RPC failure") + } + + if err := client.Start(t.Context()); err != nil { + t.Fatalf("second Start failed: %v", err) + } + defer client.ForceStop() + }) +} + +type startupRPCRequest struct { + Method string + Params json.RawMessage +} + +func newStartupRPCServer(t *testing.T) (string, func() []startupRPCRequest, func()) { + return newStartupRPCServerWithBuiltinFailure(t, false) +} + +func newStartupRPCServerWithBuiltinFailure(t *testing.T, failFirstBuiltin bool) (string, func() []startupRPCRequest, func()) { + t.Helper() + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("listen: %v", err) + } + + var mux sync.Mutex + var requests []startupRPCRequest + serverReady := make(chan *jsonrpc2.Client, 8) + var builtinSetCount int + go func() { + for { + conn, acceptErr := listener.Accept() + if acceptErr != nil { + return + } + server := jsonrpc2.NewClient(conn, conn) + record := func(method string, params json.RawMessage) { + mux.Lock() + requests = append(requests, startupRPCRequest{ + Method: method, + Params: append(json.RawMessage(nil), params...), + }) + mux.Unlock() + } + server.SetRequestHandler("connect", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + record("connect", params) + return []byte(`{"ok":true,"protocolVersion":3,"version":"test"}`), nil + }) + server.SetRequestHandler("plugins.builtin.set", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + record("plugins.builtin.set", params) + mux.Lock() + builtinSetCount++ + shouldFail := failFirstBuiltin && builtinSetCount == 1 + mux.Unlock() + if shouldFail { + return nil, &jsonrpc2.Error{Code: -32000, Message: "builtin registration failed"} + } + return []byte(`{}`), nil + }) + server.Start() + serverReady <- server + } + }() + + snapshot := func() []startupRPCRequest { + mux.Lock() + defer mux.Unlock() + return append([]startupRPCRequest(nil), requests...) + } + cleanup := func() { + listener.Close() + for { + select { + case server := <-serverReady: + server.Stop() + case <-time.After(time.Second): + return + default: + return + } + } + } + return listener.Addr().String(), snapshot, cleanup +} + +func countMethod(requests []startupRPCRequest, method string) int { + count := 0 + for _, request := range requests { + if request.Method == method { + count++ + } + } + return count +} + func TestClient_StopRequestsRuntimeShutdownForOwnedProcess(t *testing.T) { rpcClient, server, shutdownCalled := newRuntimeShutdownRpcPair(t) client := &Client{ diff --git a/go/types.go b/go/types.go index 8690695f2..c0b34586d 100644 --- a/go/types.go +++ b/go/types.go @@ -129,6 +129,11 @@ type ClientOptions struct { // location. // Ignored when connecting to an existing runtime via [URIConnection]. BaseDirectory string + // BuiltinPluginDirectories contains absolute paths to trusted plugin + // directories bundled by the host. When non-empty, Start replaces the + // runtime's complete trusted built-in plugin directory set before sessions + // can be created. + BuiltinPluginDirectories []string // LogLevel for the runtime. When empty (the default), the runtime // uses its own default level; the SDK does not pass --log-level. // Recognized values: "none", "error", "warning", "info", "debug", "all". diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java index 3dba1ea66..cdd1b9ff3 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -531,11 +531,10 @@ private CompletableFuture startCore() { private Connection startCoreBody() { Process process = null; + JsonRpcClient rpc = null; InProcessTransport inProcessTransport = null; long startNanos = System.nanoTime(); try { - JsonRpcClient rpc; - if (runtimeConnection instanceof InProcessRuntimeConnection) { // In-process runtime hosted in this process (no child process) inProcessTransport = inProcessTransportFactory.open(options); @@ -554,12 +553,13 @@ private Connection startCoreBody() { LoggingHelpers.logTiming(LOG, Level.FINE, "CopilotClient.start transport setup complete. Elapsed={Elapsed}", startNanos); - Connection connection = new Connection(rpc, process, new ServerRpc(rpc::invoke), + JsonRpcClient connectedRpc = rpc; + Connection connection = new Connection(connectedRpc, process, new ServerRpc(connectedRpc::invoke), inProcessTransport == null ? null : inProcessTransport.host()); // Register handlers for server-to-client calls RpcHandlerDispatcher dispatcher = new RpcHandlerDispatcher(sessions, lifecycleManager::dispatch, executor); - dispatcher.registerHandlers(rpc); + dispatcher.registerHandlers(connectedRpc); // Register the LLM inference request handler when configured. com.github.copilot.CopilotRequestHandler requestHandler = this.options.getRequestHandler(); @@ -567,7 +567,7 @@ private Connection startCoreBody() { if (hasLlmInference) { LlmInferenceAdapter llmAdapter = new LlmInferenceAdapter(requestHandler, () -> connection.serverRpc().llmInference, executor); - llmAdapter.registerHandlers(rpc); + llmAdapter.registerHandlers(connectedRpc); } // Register the GitHub telemetry forwarding handler when configured. @@ -575,7 +575,7 @@ private Connection startCoreBody() { .getOnGitHubTelemetry(); if (onGitHubTelemetry != null) { GitHubTelemetryAdapter telemetryAdapter = new GitHubTelemetryAdapter(onGitHubTelemetry); - telemetryAdapter.registerHandlers(rpc); + telemetryAdapter.registerHandlers(connectedRpc); } // Verify protocol version @@ -583,6 +583,15 @@ private Connection startCoreBody() { LoggingHelpers.logTiming(LOG, Level.FINE, "CopilotClient.start protocol verification complete. Elapsed={Elapsed}", startNanos); + var builtinPluginDirectories = options.getBuiltinPluginDirectories(); + if (builtinPluginDirectories != null && !builtinPluginDirectories.isEmpty()) { + var paths = new ArrayList(builtinPluginDirectories.size()); + for (var path : builtinPluginDirectories) { + paths.add(path.toString()); + } + connection.rpc.invoke("plugins.builtin.set", Map.of("paths", paths), Void.class).join(); + } + // Register as the runtime's LLM inference provider once connected. if (hasLlmInference) { connection.serverRpc().llmInference.setProvider().join(); @@ -599,6 +608,13 @@ private Connection startCoreBody() { if (process != null) { cleanupCliProcess(process, true); } + if (rpc != null) { + try { + rpc.close(); + } catch (Exception closeError) { + LOG.log(Level.FINE, "Error closing RPC after failed startup", closeError); + } + } if (inProcessTransport != null) { closeRuntimeHost(inProcessTransport.host()); } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java index ace972e5b..d3515509b 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/CopilotClientOptions.java @@ -4,11 +4,15 @@ package com.github.copilot.rpc; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Function; @@ -19,8 +23,6 @@ import com.github.copilot.CopilotExperimental; import com.github.copilot.CopilotRequestHandler; import com.github.copilot.generated.rpc.GitHubTelemetryNotification; -import java.util.Optional; -import java.util.OptionalInt; /** * Configuration options for creating a @@ -48,6 +50,7 @@ public class CopilotClientOptions { @Deprecated private boolean autoRestart; private boolean autoStart = true; + private List builtinPluginDirectories; private String[] cliArgs; private String cliPath; private String cliUrl; @@ -120,6 +123,40 @@ public CopilotClientOptions setAutoStart(boolean autoStart) { return this; } + /** + * Gets the trusted plugin directories bundled by the host. + * + * @return a copy of the configured absolute paths, or {@code null} + */ + public List getBuiltinPluginDirectories() { + return builtinPluginDirectories != null ? new ArrayList<>(builtinPluginDirectories) : null; + } + + /** + * Sets trusted plugin directories bundled by the host. Every path must be + * absolute. When non-empty, the complete set is registered during startup + * before sessions can be created. + * + * @param paths + * absolute plugin directory paths, or {@code null}/empty to disable + * @return this options instance for method chaining + */ + public CopilotClientOptions setBuiltinPluginDirectories(List paths) { + if (paths == null || paths.isEmpty()) { + this.builtinPluginDirectories = null; + return this; + } + for (Path path : paths) { + Objects.requireNonNull(path, "builtin plugin directory path must not be null"); + if (!path.isAbsolute()) { + throw new IllegalArgumentException( + "BuiltinPluginDirectories must contain only absolute paths: " + path); + } + } + this.builtinPluginDirectories = new ArrayList<>(paths); + return this; + } + /** * Gets the extra CLI arguments. *

@@ -785,6 +822,9 @@ public CopilotClientOptions clone() { CopilotClientOptions copy = new CopilotClientOptions(); copy.autoRestart = this.autoRestart; copy.autoStart = this.autoStart; + copy.builtinPluginDirectories = this.builtinPluginDirectories != null + ? new ArrayList<>(this.builtinPluginDirectories) + : null; copy.cliArgs = this.cliArgs != null ? this.cliArgs.clone() : null; copy.cliPath = this.cliPath; copy.cliUrl = this.cliUrl; diff --git a/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java b/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java new file mode 100644 index 000000000..fa353e627 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.copilot.rpc.CopilotClientOptions; + +class BuiltinPluginDirectoriesTest { + + @Test + void defaultAndEmptyDoNotCallRpc() throws Exception { + assertDoesNotCallRpc(new CopilotClientOptions()); + assertDoesNotCallRpc(new CopilotClientOptions().setBuiltinPluginDirectories(List.of())); + } + + @Test + void configuredDirectoriesCallRpcOnceBeforeStartCompletes() throws Exception { + var paths = List.of(Path.of("").toAbsolutePath().resolve("plugins/core"), + Path.of("").toAbsolutePath().resolve("plugins/github")); + + try (var server = new FakeRuntimeServer(); + var client = new CopilotClient( + new CopilotClientOptions().setCliUrl(server.url()).setBuiltinPluginDirectories(paths))) { + client.start().get(15, TimeUnit.SECONDS); + + assertEquals(1, server.builtinSetCount()); + JsonNode params = server.awaitBuiltinParams(); + assertEquals(paths.get(0).toString(), params.path("paths").get(0).asText()); + assertEquals(paths.get(1).toString(), params.path("paths").get(1).asText()); + } + } + + @Test + void relativeDirectoryIsRejected() { + assertThrows(IllegalArgumentException.class, + () -> new CopilotClientOptions().setBuiltinPluginDirectories(List.of(Path.of("plugins/core")))); + } + + private static void assertDoesNotCallRpc(CopilotClientOptions options) throws Exception { + try (var server = new FakeRuntimeServer(); var client = new CopilotClient(options.setCliUrl(server.url()))) { + client.start().get(15, TimeUnit.SECONDS); + assertEquals(0, server.builtinSetCount()); + } + } + + private static final class FakeRuntimeServer implements AutoCloseable { + + private final ServerSocket serverSocket; + private final Thread acceptThread; + private final CompletableFuture ready = new CompletableFuture<>(); + private final CompletableFuture builtinParams = new CompletableFuture<>(); + private final AtomicInteger builtinSetCount = new AtomicInteger(); + + FakeRuntimeServer() throws IOException { + serverSocket = new ServerSocket(0); + acceptThread = new Thread(this::acceptLoop, "builtin-plugin-runtime"); + acceptThread.setDaemon(true); + acceptThread.start(); + } + + String url() { + return "127.0.0.1:" + serverSocket.getLocalPort(); + } + + int builtinSetCount() { + return builtinSetCount.get(); + } + + JsonNode awaitBuiltinParams() throws Exception { + return builtinParams.get(15, TimeUnit.SECONDS); + } + + private void acceptLoop() { + try { + Socket socket = serverSocket.accept(); + JsonRpcClient server = JsonRpcClient.fromSocket(socket); + server.registerMethodHandler("connect", (id, params) -> respond(server, id, + Map.of("ok", true, "protocolVersion", 3, "version", "test"))); + server.registerMethodHandler("plugins.builtin.set", (id, params) -> { + builtinSetCount.incrementAndGet(); + builtinParams.complete(params); + respond(server, id, Map.of()); + }); + ready.complete(server); + } catch (IOException e) { + ready.completeExceptionally(e); + builtinParams.completeExceptionally(e); + } + } + + private static void respond(JsonRpcClient server, String id, Object result) { + if (id == null) { + return; + } + try { + server.sendResponse(id, result); + } catch (IOException e) { + // Connection teardown can race the response during test cleanup. + } + } + + @Override + public void close() throws Exception { + JsonRpcClient server = ready.getNow(null); + if (server != null) { + server.close(); + } + serverSocket.close(); + } + } +} diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 1182b4106..30095186e 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -16,7 +16,7 @@ import { randomUUID } from "node:crypto"; import { existsSync } from "node:fs"; import { createRequire } from "node:module"; import { Socket } from "node:net"; -import { dirname, join } from "node:path"; +import { dirname, isAbsolute, join } from "node:path"; import { fileURLToPath } from "node:url"; import { createMessageConnection, @@ -522,6 +522,7 @@ export class CopilotClient { /** Connection-level session filesystem config, set via constructor option. */ private sessionFsConfig: SessionFsConfig | null = null; private requestHandler: CopilotRequestHandler | null = null; + private builtinPluginDirectories: string[] = []; private onGitHubTelemetry?: (notification: GitHubTelemetryNotification) => void | Promise; private clientGlobalHandlers: import("./generated/rpc.js").ClientGlobalApiHandlers = {}; @@ -682,6 +683,16 @@ export class CopilotClient { if (options.sessionFs) { this.validateSessionFsConfig(options.sessionFs); } + if (options.builtinPluginDirectories) { + for (const path of options.builtinPluginDirectories) { + if (!isAbsolute(path)) { + throw new Error( + `builtinPluginDirectories must contain only absolute paths: ${path}` + ); + } + } + this.builtinPluginDirectories = [...options.builtinPluginDirectories]; + } // Pre-parse the URI host/port and mark as external if applicable. if (conn.kind === "uri") { @@ -894,6 +905,17 @@ export class CopilotClient { // Verify protocol version compatibility await this.verifyProtocolVersion(); + if (this.builtinPluginDirectories.length > 0) { + try { + await this.connection!.sendRequest("plugins.builtin.set", { + paths: this.builtinPluginDirectories, + }); + } catch (error) { + await this.forceStop(); + throw error; + } + } + // If a session filesystem provider was configured, register it if (this.sessionFsConfig) { await this.connection!.sendRequest("sessionFs.setProvider", { diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 167e52d87..8149c36da 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -304,6 +304,13 @@ export interface CopilotClientOptions { */ baseDirectory?: string; + /** + * Absolute paths to trusted plugin directories bundled by the host. + * When non-empty, the complete set is registered with the runtime during + * startup before any sessions can be created. + */ + builtinPluginDirectories?: readonly string[]; + /** * Log level for the Copilot runtime. When omitted, the runtime uses its * own default (currently `"info"`). diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 254c126ed..49ec169c4 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -3,7 +3,7 @@ import { EventEmitter } from "node:events"; import { PassThrough } from "stream"; import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; -import { join } from "node:path"; +import { join, resolve } from "node:path"; import { describe, expect, it, onTestFinished, vi } from "vitest"; import { approveAll, @@ -57,6 +57,51 @@ describe("approveAll", () => { }); describe("CopilotClient", () => { + async function startWithMockConnection( + builtinPluginDirectories?: readonly string[] + ): Promise> { + const client = new CopilotClient({ + connection: RuntimeConnection.forUri("localhost:1234"), + builtinPluginDirectories, + }); + const sendRequest = vi.fn(async () => ({})); + vi.spyOn(client as any, "connectToServer").mockImplementation(async () => { + (client as any).connection = { sendRequest }; + }); + vi.spyOn(client as any, "verifyProtocolVersion").mockResolvedValue(undefined); + + await client.start(); + return sendRequest; + } + + it.each([undefined, []])( + "does not configure built-in plugin directories when unset or empty", + async (builtinPluginDirectories) => { + const sendRequest = await startWithMockConnection(builtinPluginDirectories); + + expect(sendRequest).not.toHaveBeenCalledWith("plugins.builtin.set", expect.anything()); + } + ); + + it("configures built-in plugin directories before start completes", async () => { + const paths = [resolve("plugins/core"), resolve("plugins/github")]; + + const sendRequest = await startWithMockConnection(paths); + + expect(sendRequest).toHaveBeenCalledTimes(1); + expect(sendRequest).toHaveBeenCalledWith("plugins.builtin.set", { paths }); + }); + + it("rejects relative built-in plugin directories", () => { + expect( + () => + new CopilotClient({ + connection: RuntimeConnection.forUri("localhost:1234"), + builtinPluginDirectories: ["plugins/core"], + }) + ).toThrow(/builtinPluginDirectories.*absolute paths.*plugins\/core/); + }); + it("disposes the stdio connection when child stdin emits an error", async () => { const client = new CopilotClient(); onTestFinished(() => client.forceStop()); diff --git a/python/copilot/client.py b/python/copilot/client.py index 415f44ef5..6cdd765c3 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -655,6 +655,7 @@ class _CopilotClientOptions: env: dict[str, str] | None = None github_token: str | None = None base_directory: str | None = None + builtin_plugin_directories: tuple[str, ...] = () use_logged_in_user: bool | None = None telemetry: TelemetryConfig | None = None session_fs: SessionFsConfig | None = None @@ -1427,6 +1428,7 @@ def __init__( env: dict[str, str] | None = None, github_token: str | None = None, base_directory: str | None = None, + builtin_plugin_directories: Sequence[str] | None = None, use_logged_in_user: bool | None = None, telemetry: TelemetryConfig | None = None, session_fs: SessionFsConfig | None = None, @@ -1462,6 +1464,9 @@ def __init__( config, etc.). Sets the ``COPILOT_HOME`` environment variable on the spawned runtime. When ``None``, the runtime defaults to ``~/.copilot``. + builtin_plugin_directories: Absolute paths to trusted plugin + directories bundled by the host. When non-empty, the complete + set is registered during startup before sessions can be created. use_logged_in_user: Use the logged-in user for authentication. ``None`` (default) resolves to ``True`` unless ``github_token`` is set. @@ -1509,6 +1514,7 @@ def __init__( env=env, github_token=github_token, base_directory=base_directory, + builtin_plugin_directories=tuple(builtin_plugin_directories or ()), use_logged_in_user=use_logged_in_user, telemetry=telemetry, session_fs=session_fs, @@ -1525,6 +1531,11 @@ def __init__( else _resolve_default_connection(os.environ) ) _validate_environment_options(options, connection) + for path in options.builtin_plugin_directories: + if not os.path.isabs(path): + raise ValueError( + f"builtin_plugin_directories must contain only absolute paths: {path}" + ) _require_storage_for_empty_mode( mode=options.mode, base_directory=options.base_directory, @@ -1807,6 +1818,17 @@ async def start(self) -> None: start_time, ) + if self._options.builtin_plugin_directories: + assert self._client is not None + try: + await self._client.request( + "plugins.builtin.set", + {"paths": list(self._options.builtin_plugin_directories)}, + ) + except Exception: + await self.force_stop() + raise + if self._session_fs_config: session_fs_start = time.perf_counter() await self._set_session_fs_provider() diff --git a/python/test_client.py b/python/test_client.py index 893b82af1..cf4bdf192 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -6,6 +6,7 @@ import asyncio import inspect +import os from datetime import UTC, datetime from tempfile import TemporaryDirectory from unittest.mock import AsyncMock, Mock, patch @@ -57,6 +58,50 @@ def test_inprocess_connection_has_no_child_process_options(): assert not hasattr(connection, "args") +class TestBuiltinPluginDirectories: + @staticmethod + async def _start_client(paths=None): + client = CopilotClient( + connection=RuntimeConnection.for_uri("localhost:1234"), + builtin_plugin_directories=paths, + ) + client._connect_to_server = AsyncMock() + client._verify_protocol_version = AsyncMock() + client._client = Mock() + client._client.request = AsyncMock(return_value={}) + + await client.start() + return client + + @pytest.mark.asyncio + @pytest.mark.parametrize("paths", [None, []]) + async def test_default_or_empty_does_not_call_rpc(self, paths): + client = await self._start_client(paths) + + client._client.request.assert_not_awaited() + + @pytest.mark.asyncio + async def test_configured_paths_call_rpc_once_before_start_completes(self): + paths = [ + os.path.abspath("plugins/core"), + os.path.abspath("plugins/github"), + ] + + client = await self._start_client(paths) + + client._client.request.assert_awaited_once_with( + "plugins.builtin.set", + {"paths": paths}, + ) + + def test_relative_path_is_rejected(self): + with pytest.raises(ValueError, match="builtin_plugin_directories.*absolute paths"): + CopilotClient( + connection=RuntimeConnection.for_uri("localhost:1234"), + builtin_plugin_directories=["plugins/core"], + ) + + class TestClientShutdown: @pytest.mark.asyncio async def test_stop_requests_runtime_shutdown_for_owned_process(self): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index cafa3c596..9e3041fec 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -254,6 +254,11 @@ pub struct ClientOptions { pub env_remove: Vec, /// Extra flags for child-process transports. pub extra_args: Vec, + /// Absolute paths to trusted plugin directories bundled by the host. + /// + /// When non-empty, [`Client::start`] replaces the runtime's complete + /// trusted built-in plugin directory set before sessions can be created. + pub builtin_plugin_directories: Vec, /// Transport mode used to communicate with the CLI server. pub transport: Transport, /// GitHub token for authentication. When set, the SDK passes the token @@ -368,6 +373,10 @@ impl std::fmt::Debug for ClientOptions { .field("env", &self.env) .field("env_remove", &self.env_remove) .field("extra_args", &self.extra_args) + .field( + "builtin_plugin_directories", + &self.builtin_plugin_directories, + ) .field("transport", &self.transport) .field( "github_token", @@ -632,6 +641,7 @@ impl Default for ClientOptions { env: Vec::new(), env_remove: Vec::new(), extra_args: Vec::new(), + builtin_plugin_directories: Vec::new(), transport: Transport::default(), github_token: None, use_logged_in_user: None, @@ -724,6 +734,19 @@ impl ClientOptions { self } + /// Set trusted plugin directories bundled by the host. + /// + /// Every path must be absolute; invalid paths are rejected by + /// [`Client::start`]. + pub fn with_builtin_plugin_directories(mut self, paths: I) -> Self + where + I: IntoIterator, + P: Into, + { + self.builtin_plugin_directories = paths.into_iter().map(Into::into).collect(); + self + } + /// Transport mode used to communicate with the CLI server. See [`Transport`]. pub fn with_transport(mut self, transport: Transport) -> Self { self.transport = transport; @@ -1071,6 +1094,30 @@ impl Client { if let Some(cfg) = &options.session_fs { validate_session_fs_config(cfg)?; } + let builtin_plugin_directories = options + .builtin_plugin_directories + .iter() + .map(|path| { + if !path.is_absolute() { + return Err(Error::with_message( + ErrorKind::InvalidConfig, + format!( + "builtin_plugin_directories must contain only absolute paths: {}", + path.display() + ), + )); + } + path.to_str().map(str::to_owned).ok_or_else(|| { + Error::with_message( + ErrorKind::InvalidConfig, + format!( + "builtin_plugin_directories must contain valid UTF-8 paths: {}", + path.display() + ), + ) + }) + }) + .collect::>>()?; // Auth options only make sense when the SDK spawns the CLI; with an // external server, the server manages its own auth. if matches!(options.transport, Transport::External { .. }) { @@ -1333,6 +1380,14 @@ impl Client { elapsed_ms = start_time.elapsed().as_millis(), "Client::start protocol verification complete" ); + if !builtin_plugin_directories.is_empty() { + client + .call( + "plugins.builtin.set", + Some(serde_json::json!({ "paths": builtin_plugin_directories })), + ) + .await?; + } if let Some(cfg) = session_fs_config { let session_fs_start = Instant::now(); let capabilities = cfg.capabilities.as_ref().map(|c| { diff --git a/rust/tests/builtin_plugin_directories_test.rs b/rust/tests/builtin_plugin_directories_test.rs new file mode 100644 index 000000000..f1310f9b0 --- /dev/null +++ b/rust/tests/builtin_plugin_directories_test.rs @@ -0,0 +1,135 @@ +#![allow(clippy::unwrap_used)] + +use std::path::PathBuf; + +use github_copilot_sdk::{CliProgram, Client, ClientOptions, ErrorKind, Transport}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::net::TcpListener; + +async fn read_framed(reader: &mut (impl AsyncRead + Unpin)) -> serde_json::Value { + let mut header = String::new(); + loop { + let mut byte = [0u8; 1]; + reader.read_exact(&mut byte).await.unwrap(); + header.push(byte[0] as char); + if header.ends_with("\r\n\r\n") { + break; + } + } + let length = header + .trim() + .strip_prefix("Content-Length: ") + .unwrap() + .parse() + .unwrap(); + let mut body = vec![0; length]; + reader.read_exact(&mut body).await.unwrap(); + serde_json::from_slice(&body).unwrap() +} + +async fn write_result( + writer: &mut (impl AsyncWrite + Unpin), + request: &serde_json::Value, + result: serde_json::Value, +) { + let body = serde_json::to_vec(&serde_json::json!({ + "jsonrpc": "2.0", + "id": request["id"], + "result": result, + })) + .unwrap(); + writer + .write_all(format!("Content-Length: {}\r\n\r\n", body.len()).as_bytes()) + .await + .unwrap(); + writer.write_all(&body).await.unwrap(); + writer.flush().await.unwrap(); +} + +async fn run_start(paths: Option>) -> Vec { + let listener = TcpListener::bind(("127.0.0.1", 0)).await.unwrap(); + let address = listener.local_addr().unwrap(); + let expect_builtin = paths.as_ref().is_some_and(|paths| !paths.is_empty()); + + let server = tokio::spawn(async move { + let (stream, _) = listener.accept().await.unwrap(); + let (mut reader, mut writer) = tokio::io::split(stream); + let mut requests = Vec::new(); + + let connect = read_framed(&mut reader).await; + write_result( + &mut writer, + &connect, + serde_json::json!({ "ok": true, "protocolVersion": 3, "version": "test" }), + ) + .await; + requests.push(connect); + + if expect_builtin { + let builtin = read_framed(&mut reader).await; + write_result(&mut writer, &builtin, serde_json::json!({})).await; + requests.push(builtin); + } + requests + }); + + let mut options = ClientOptions::new() + .with_program(CliProgram::Path(std::env::current_exe().unwrap())) + .with_transport(Transport::External { + host: address.ip().to_string(), + port: address.port(), + connection_token: None, + }); + if let Some(paths) = paths { + options = options.with_builtin_plugin_directories(paths); + } + let client = Client::start(options).await.unwrap(); + let requests = server.await.unwrap(); + client.force_stop(); + requests +} + +#[tokio::test] +async fn default_and_empty_do_not_call_rpc() { + for paths in [None, Some(Vec::new())] { + let requests = run_start(paths).await; + assert_eq!(requests.len(), 1); + assert_eq!(requests[0]["method"], "connect"); + } +} + +#[tokio::test] +async fn configured_directories_call_rpc_once_before_start_completes() { + let cwd = std::env::current_dir().unwrap(); + let paths = vec![cwd.join("plugins/core"), cwd.join("plugins/github")]; + + let requests = run_start(Some(paths.clone())).await; + + assert_eq!(requests.len(), 2); + assert_eq!(requests[0]["method"], "connect"); + assert_eq!(requests[1]["method"], "plugins.builtin.set"); + assert_eq!( + requests[1]["params"], + serde_json::json!({ + "paths": paths + .iter() + .map(|path| path.to_str().unwrap()) + .collect::>() + }) + ); +} + +#[tokio::test] +async fn relative_directory_is_rejected() { + let options = ClientOptions::new() + .with_program(CliProgram::Path(std::env::current_exe().unwrap())) + .with_builtin_plugin_directories(["plugins/core"]); + + let error = match Client::start(options).await { + Ok(_) => panic!("relative path unexpectedly accepted"), + Err(error) => error, + }; + + assert_eq!(error.kind(), &ErrorKind::InvalidConfig); + assert!(error.to_string().contains("absolute paths")); +} From a550258d5c37bd662197536992a23d633bfe5804 Mon Sep 17 00:00:00 2001 From: Aymen Date: Fri, 14 Aug 2026 13:04:16 +0000 Subject: [PATCH 64/72] sdk: Forward decisionContext on permission replies across languages (#2294) * feat(rust): forward decisionContext on permission replies The runtime emits `auto_approval_decision` telemetry only when a client supplies an explicit `decisionContext` alongside its permission reply. The generated wire types already carry the optional field, but the hand-written reply path built a fixed three-key JSON literal and had no way for a PermissionHandler to attribute its decision. Add `PermissionResult::AttributedDecision` plus a `with_context` builder, and forward the context as a top-level sibling of `result`. When no context is supplied the emitted params are byte-identical to before, so legacy behavior is preserved exactly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * sdk: Forward decisionContext on permission replies across languages Permission handlers can now attach optional provenance describing how and where a decision was reached. The SDK forwards it to the runtime as a sibling of `result` -- never nested inside it -- so auto-approval decisions made programmatically can be attributed. The wire schema and every language's generated types already accepted the field; only the hand-written reply paths never populated it. No schema, codegen, or protocol version change is required. Fully additive: handlers returning a plain decision emit a payload byte-identical to before, with no `decisionContext` key at all. No-result suppression is preserved in every language. Per CONTRIBUTING.md, the feature is implemented in sync across all six SDKs: - Rust: PermissionResult::AttributedDecision + with_context() - Node: AttributedPermissionResult + withDecisionContext() - Python: AttributedPermissionResult + with_decision_context() - Go: AttributedPermissionResult + WithDecisionContext() - .NET: PermissionDecision.WithContext() - Java: PermissionRequestResult.withContext() Each language gains focused unit tests asserting the sibling placement, the byte-identical legacy payload, replace-not-nest on re-application, and preserved no-result suppression. Node and Rust add end-to-end coverage against a CLI carrying the runtime-side support. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk(java): Reject null in withContext to match the other SDKs Java accepted null as a "clear" operation while .NET rejects it and the other SDKs disallow it at the type level. Since null is Java's default, an uninitialized variable would have silently dropped the context -- producing exactly the unattributed telemetry this feature removes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk: Unwrap value-form attribution in Go and seal the Rust enum Go embedded the decision interface in AttributedPermissionResult, which promotes the interface methods to the value type. A handler returning `*WithDecisionContext(...)` therefore satisfied rpc.PermissionDecision but slipped past the pointer-only type assertion: the wrapper itself was sent as `result` and the context was silently dropped. Both the unwrap in the session and the replace-not-nest check now accept either form, with regression tests that fail against the pointer-only code. Rust PermissionResult gains #[non_exhaustive], matching the convention used throughout this crate, so downstream exhaustive matches keep compiling as variants are added. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk(dotnet): Drop fluent WithContext in favor of the settable property The hand-written .NET SDK has no other fluent `With*` builders, so adding one here introduced a pattern that exists nowhere else in the surface. Java and Rust keep their fluent forms because those match long-standing convention in each of those SDKs. Callers now set the public `DecisionContext` property through an object initializer, which is what the class documentation already recommends for richer decisions. The wire format is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk: Name attribution helpers after each SDK's own conventions The Go, Node, and Python helpers were named `WithDecisionContext` and friends, a shape none of those SDKs use. In Go a `WithX` function conventionally builds a functional option rather than decorating a value, and there were no `With` functions in the package at all. Node and Python had no `with`-prefixed helper either. Each now follows the constructor naming its own SDK already uses: `NewAttributedPermissionResult` alongside `NewCanvasError`, `createAttributedPermissionResult` alongside `createCanvas`, and `create_attributed_permission_result` alongside `create_session_fs_adapter`. The Node wrapper also gains a `kind: "attributed"` discriminant so it is narrowed the same way as every other union in that SDK, instead of by testing for the presence of a property. Java and Rust keep their fluent methods, which match long-standing convention in each of those SDKs. Behavior and wire format are unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk(go): Centralize attribution unwrapping in one helper The pointer/value type switch was duplicated verbatim in NewAttributedPermissionResult and the session permission dispatch. Embedding an interface promotes its methods to the value type too, so both forms satisfy rpc.PermissionDecision and both must be unwrapped -- missing the value case is what produced the bug caught in review. Fold both copies into splitAttribution so that hazard is stated and handled in exactly one place. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk(java): Rename withContext to setDecisionContext The Java SDK uses setX for mutators (589 of them); withX appears twice and both return a copy rather than mutating in place. withContext was the odd one out on both counts, and did not match its own getter or the sibling setKind/setRules/setFeedback on this class. Also drop the requireNonNull. The other setters here do not null-check, and null now means "no context" in every other SDK, so throwing made Java the outlier rather than the consistent one. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * sdk(rust): Make AttributedDecision a struct-style variant Hand-written Rust here has 157 enum variants: 89 unit, 35 tuple with exactly one payload, and 33 struct-style. Every variant carrying two or more values uses the struct form, so a two-payload tuple was the only one of its kind. Name the payloads instead. Construction and both read sites now say which value they mean rather than relying on position. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * Preserve Rust permission handler compatibility Add a separate attributed permission handler path so clients can forward decision context without changing the existing PermissionResult enum or PermissionHandler contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * Simplify Rust permission attribution Keep PermissionHandler as the single dispatch API and carry decision context through PermissionResult. Replace the ineffective rejection E2E test with an exact fake-server wire assertion. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 * Unify Rust permission decisions Store optional decision context on the existing Decision variant so every decision follows one semantic path and downstream matches receive a compiler-guided migration. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 --------- Co-authored-by: Aymen Furter Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79152db2-4cc7-4777-983a-655fae5b68c9 --- docs/troubleshooting/compatibility.md | 2 +- dotnet/src/PermissionDecision.cs | 9 + dotnet/src/Session.cs | 2 +- .../test/Unit/ClientSessionLifetimeTests.cs | 206 ++++++++++++++ go/permission_context_test.go | 260 ++++++++++++++++++ go/permissions.go | 56 ++++ go/session.go | 9 +- go/types.go | 41 +++ .../com/github/copilot/CopilotSession.java | 2 +- .../copilot/rpc/PermissionRequestResult.java | 42 +++ ...ssionRequestResultDecisionContextTest.java | 97 +++++++ nodejs/src/index.ts | 6 + nodejs/src/session.ts | 15 +- nodejs/src/types.ts | 51 +++- nodejs/test/client.test.ts | 86 ++++++ nodejs/test/e2e/permissions.e2e.test.ts | 60 +++- python/copilot/__init__.py | 12 + python/copilot/session.py | 50 +++- python/test_permission_decision_context.py | 99 +++++++ rust/src/handler.rs | 102 +++++-- rust/src/permission.rs | 38 ++- rust/src/session.rs | 166 ++++++++--- rust/src/types.rs | 64 ++++- rust/tests/e2e/permissions.rs | 7 +- rust/tests/session_test.rs | 64 ++++- ...cision_annotated_with_decisioncontext.yaml | 24 ++ 26 files changed, 1477 insertions(+), 93 deletions(-) create mode 100644 go/permission_context_test.go create mode 100644 java/sdk/src/test/java/com/github/copilot/rpc/PermissionRequestResultDecisionContextTest.java create mode 100644 python/test_permission_decision_context.py create mode 100644 test/snapshots/permissions/should_honor_a_decision_annotated_with_decisioncontext.yaml diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index 3238c59d9..da8bf0daa 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -77,7 +77,7 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b | System message | `systemMessage` config | Append or replace | | Custom provider | `provider` config | BYOK support | | Infinite sessions | `infiniteSessions` config | Auto-compaction | -| Permission handler | `onPermissionRequest` | Approve/deny requests | +| Permission handler | `onPermissionRequest` | Approve/deny requests; optionally attach a `decisionContext` for auto-approval telemetry | | User input handler | `onUserInputRequest` | Handle ask_user | | Skills | `skillDirectories` config | Custom skills | | Disabled skills | `disabledSkills` config | Disable specific skills | diff --git a/dotnet/src/PermissionDecision.cs b/dotnet/src/PermissionDecision.cs index 54e123791..3eb1d0e08 100644 --- a/dotnet/src/PermissionDecision.cs +++ b/dotnet/src/PermissionDecision.cs @@ -43,4 +43,13 @@ public static PermissionDecision Reject(string? feedback = null) => /// connected client to answer instead. ///

public static PermissionDecision NoResult() => new PermissionDecisionNoResult(); + + /// + /// Optional provenance describing how and where this decision was made. + /// This is never serialized as part of the decision itself: the SDK forwards + /// it to the runtime as a sibling of result so that auto-approval + /// telemetry can be attributed correctly. + /// + [JsonIgnore] + public PermissionDecisionContext? DecisionContext { get; set; } } diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index 7c34ded16..0ce10c290 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -954,7 +954,7 @@ private async Task ExecutePermissionAndRespondAsync(string requestId, Permission return; } var responseRpcTimestamp = Stopwatch.GetTimestamp(); - await Rpc.Permissions.HandlePendingPermissionRequestAsync(requestId, decision); + await Rpc.Permissions.HandlePendingPermissionRequestAsync(requestId, decision, decision.DecisionContext); LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null, "CopilotSession.ExecutePermissionAndRespondAsync response sent successfully. Elapsed={Elapsed}, SessionId={SessionId}, RequestId={RequestId}", responseRpcTimestamp, diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index da04ff38b..a561ee44b 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -585,6 +585,208 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() Assert.True(invocation.ManagedSettingsEnabled); } + [Fact] + public async Task PermissionResponse_Forwards_DecisionContext_As_Sibling_Of_Result() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = (_, _) => Task.FromResult( + new PermissionDecisionApproveOnce + { + DecisionContext = new PermissionDecisionContext + { + Outcome = PermissionDecisionOutcome.AutoApproved, + Source = PermissionDecisionSource.HostPolicy, + Surface = PermissionDecisionSurface.Sdk + } + }) + }); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "req-with-context" + } + }); + + var request = await WaitForRequestAsync(server, "session.permissions.handlePendingPermissionRequest"); + + Assert.True(request.Params.TryGetProperty("decisionContext", out var decisionContext)); + Assert.Equal("auto_approved", decisionContext.GetProperty("outcome").GetString()); + Assert.Equal("host_policy", decisionContext.GetProperty("source").GetString()); + Assert.Equal("sdk", decisionContext.GetProperty("surface").GetString()); + + var result = request.Params.GetProperty("result"); + Assert.Equal("approve-once", result.GetProperty("kind").GetString()); + Assert.False(result.TryGetProperty("decisionContext", out _)); + } + + [Fact] + public async Task PermissionResponse_Omits_DecisionContext_When_Not_Supplied() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = (_, _) => Task.FromResult(PermissionDecision.ApproveOnce()) + }); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "req-no-context" + } + }); + + var request = await WaitForRequestAsync(server, "session.permissions.handlePendingPermissionRequest"); + + Assert.False(request.Params.TryGetProperty("decisionContext", out _)); + var result = request.Params.GetProperty("result"); + Assert.Equal("approve-once", result.GetProperty("kind").GetString()); + Assert.False(result.TryGetProperty("decisionContext", out _)); + } + + [Fact] + public async Task PermissionResponse_Uses_Latest_Context_When_Reassigned() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = (_, _) => + { + var decision = new PermissionDecisionApproveOnce + { + DecisionContext = new PermissionDecisionContext + { + Outcome = PermissionDecisionOutcome.PromptedUser, + Source = PermissionDecisionSource.HumanResponse, + Surface = PermissionDecisionSurface.Tui + } + }; + decision.DecisionContext = new PermissionDecisionContext + { + Outcome = PermissionDecisionOutcome.AutoApproved, + Source = PermissionDecisionSource.HostPolicy, + Surface = PermissionDecisionSurface.Sdk + }; + return Task.FromResult(decision); + } + }); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "req-replace-context" + } + }); + + var request = await WaitForRequestAsync(server, "session.permissions.handlePendingPermissionRequest"); + + var decisionContext = request.Params.GetProperty("decisionContext"); + Assert.Equal("auto_approved", decisionContext.GetProperty("outcome").GetString()); + Assert.Equal("host_policy", decisionContext.GetProperty("source").GetString()); + Assert.Equal("sdk", decisionContext.GetProperty("surface").GetString()); + } + + [Fact] + public async Task PermissionResponse_Is_Suppressed_For_NoResult_Even_With_Context() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + var handlerInvoked = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = (_, _) => + { + handlerInvoked.TrySetResult(); + return Task.FromResult( + new PermissionDecisionNoResult + { + DecisionContext = new PermissionDecisionContext + { + Outcome = PermissionDecisionOutcome.PromptedUser, + Source = PermissionDecisionSource.HumanResponse, + Surface = PermissionDecisionSurface.Sdk + } + }); + } + }); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "req-no-result" + } + }); + + await handlerInvoked.Task.WaitAsync(TimeSpan.FromSeconds(5)); + // Give the send path a chance to (incorrectly) fire before asserting suppression. + await Task.Delay(200); + + Assert.DoesNotContain(server.Requests, request => request.Method == "session.permissions.handlePendingPermissionRequest"); + } + + [Fact] + public async Task PermissionResponse_Never_Nests_DecisionContext_Inside_Result() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = (_, _) => Task.FromResult( + new PermissionDecisionReject + { + Feedback = "denied by policy", + DecisionContext = new PermissionDecisionContext + { + Outcome = PermissionDecisionOutcome.AutopilotDenied, + Source = PermissionDecisionSource.HostPolicy, + Surface = PermissionDecisionSurface.Sdk + } + }) + }); + + DispatchEvent(session, new PermissionRequestedEvent + { + Data = new PermissionRequestedData + { + PermissionRequest = new PermissionRequest { Kind = "read" }, + RequestId = "req-reject-context" + } + }); + + var request = await WaitForRequestAsync(server, "session.permissions.handlePendingPermissionRequest"); + + var result = request.Params.GetProperty("result"); + Assert.Equal("reject", result.GetProperty("kind").GetString()); + Assert.Equal("denied by policy", result.GetProperty("feedback").GetString()); + // The context provenance must never be serialized inside the decision itself. + Assert.False(result.TryGetProperty("decisionContext", out _)); + // It is forwarded as a sibling instead. + Assert.True(request.Params.TryGetProperty("decisionContext", out _)); + } + [Fact] public async Task CreateSessionAsync_Omits_ManagedSettings_When_Unset() { @@ -843,6 +1045,10 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel { ["success"] = true }, + "session.permissions.handlePendingPermissionRequest" => new Dictionary + { + ["success"] = true + }, "session.delete" => new Dictionary { ["success"] = true diff --git a/go/permission_context_test.go b/go/permission_context_test.go new file mode 100644 index 000000000..16c6d2d59 --- /dev/null +++ b/go/permission_context_test.go @@ -0,0 +1,260 @@ +package copilot + +import ( + "encoding/json" + "fmt" + "io" + "testing" + "time" + + "github.com/github/copilot-sdk/go/internal/jsonrpc2" + "github.com/github/copilot-sdk/go/rpc" +) + +// runPermissionExchange drives executePermissionAndRespond with the supplied +// handler and captures the raw JSON-RPC request frame the SDK emits (if any). +// The second return value reports whether a request was sent at all, so tests +// can assert that no-result decisions suppress the response entirely. +func runPermissionExchange(t *testing.T, handler PermissionHandlerFunc) (frame []byte, sent bool) { + t.Helper() + + stdinR, stdinW := io.Pipe() + stdoutR, stdoutW := io.Pipe() + t.Cleanup(func() { + stdinR.Close() + stdinW.Close() + stdoutR.Close() + stdoutW.Close() + }) + + client := jsonrpc2.NewClient(stdinW, stdoutR) + client.Start() + t.Cleanup(client.Stop) + + session := &Session{ + SessionID: "session-1", + client: client, + RPC: rpc.NewSessionRPC(client, "session-1"), + } + + frameCh := make(chan []byte, 1) + go func() { + captured, err := readTestJSONRPCFrame(stdinR) + if err != nil { + return + } + var request struct { + ID json.RawMessage `json:"id"` + } + _ = json.Unmarshal(captured, &request) + // Publish the captured frame before unblocking the RPC round trip so a + // sent response is always observable before executePermissionAndRespond + // returns. + frameCh <- captured + response := map[string]any{ + "jsonrpc": "2.0", + "id": json.RawMessage(request.ID), + "result": map[string]any{"applied": true}, + } + data, _ := json.Marshal(response) + _, _ = fmt.Fprintf(stdoutW, "Content-Length: %d\r\n\r\n%s", len(data), data) + }() + + done := make(chan struct{}) + go func() { + session.executePermissionAndRespond("permission-1", nil, handler) + close(done) + }() + + select { + case captured := <-frameCh: + return captured, true + case <-done: + select { + case captured := <-frameCh: + return captured, true + default: + return nil, false + } + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for permission response") + return nil, false + } +} + +// paramsOf extracts the top-level params object from a JSON-RPC request frame. +func paramsOf(t *testing.T, frame []byte) map[string]json.RawMessage { + t.Helper() + var request struct { + Method string `json:"method"` + Params map[string]json.RawMessage `json:"params"` + } + if err := json.Unmarshal(frame, &request); err != nil { + t.Fatalf("failed to unmarshal request frame: %v", err) + } + if request.Method != "session.permissions.handlePendingPermissionRequest" { + t.Fatalf("unexpected method %q", request.Method) + } + return request.Params +} + +func sampleDecisionContext() *rpc.PermissionDecisionContext { + return &rpc.PermissionDecisionContext{ + Outcome: PermissionDecisionOutcomeAutoApproved, + Source: PermissionDecisionSourceHostPolicy, + Surface: PermissionDecisionSurfaceSDK, + } +} + +func TestPermissionDecisionContextForwardedAsSiblingOfResult(t *testing.T) { + frame, sent := runPermissionExchange(t, func(PermissionRequest, PermissionInvocation) (rpc.PermissionDecision, error) { + return NewAttributedPermissionResult(&rpc.PermissionDecisionApproveOnce{}, sampleDecisionContext()), nil + }) + if !sent { + t.Fatal("expected a permission response to be sent") + } + + params := paramsOf(t, frame) + + // decisionContext must be a top-level sibling of result. + rawContext, ok := params["decisionContext"] + if !ok { + t.Fatal("expected decisionContext to be present as a top-level sibling of result") + } + var context rpc.PermissionDecisionContext + if err := json.Unmarshal(rawContext, &context); err != nil { + t.Fatalf("failed to unmarshal decisionContext: %v", err) + } + if context.Outcome != PermissionDecisionOutcomeAutoApproved || + context.Source != PermissionDecisionSourceHostPolicy || + context.Surface != PermissionDecisionSurfaceSDK { + t.Fatalf("unexpected decisionContext contents: %#v", context) + } + + // result must exist and must NOT contain a nested decisionContext. + rawResult, ok := params["result"] + if !ok { + t.Fatal("expected result to be present") + } + var result map[string]json.RawMessage + if err := json.Unmarshal(rawResult, &result); err != nil { + t.Fatalf("failed to unmarshal result: %v", err) + } + if _, nested := result["decisionContext"]; nested { + t.Fatal("decisionContext must not be nested inside result") + } +} + +func TestPermissionDecisionContextOmittedWithoutAttribution(t *testing.T) { + frame, sent := runPermissionExchange(t, func(PermissionRequest, PermissionInvocation) (rpc.PermissionDecision, error) { + return &rpc.PermissionDecisionApproveOnce{}, nil + }) + if !sent { + t.Fatal("expected a permission response to be sent") + } + + params := paramsOf(t, frame) + if _, ok := params["decisionContext"]; ok { + t.Fatal("expected decisionContext to be absent when no context is supplied") + } + if _, ok := params["result"]; !ok { + t.Fatal("expected result to be present") + } +} + +func TestAttributedResultReplacesRatherThanNests(t *testing.T) { + first := &rpc.PermissionDecisionContext{ + Outcome: PermissionDecisionOutcomePromptedUser, + Source: PermissionDecisionSourceHumanResponse, + Surface: PermissionDecisionSurfaceTui, + } + second := sampleDecisionContext() + + wrapped := NewAttributedPermissionResult(NewAttributedPermissionResult(&rpc.PermissionDecisionApproveOnce{}, first), second) + + if wrapped.DecisionContext != second { + t.Fatalf("expected the second context to replace the first, got %#v", wrapped.DecisionContext) + } + // The underlying decision must be the plain approve-once, not another wrapper. + if _, ok := wrapped.PermissionDecision.(*rpc.PermissionDecisionApproveOnce); !ok { + t.Fatalf("expected unwrapped decision to be *rpc.PermissionDecisionApproveOnce, got %T", wrapped.PermissionDecision) + } + + frame, sent := runPermissionExchange(t, func(PermissionRequest, PermissionInvocation) (rpc.PermissionDecision, error) { + return wrapped, nil + }) + if !sent { + t.Fatal("expected a permission response to be sent") + } + params := paramsOf(t, frame) + rawContext, ok := params["decisionContext"] + if !ok { + t.Fatal("expected decisionContext to be present") + } + var context rpc.PermissionDecisionContext + if err := json.Unmarshal(rawContext, &context); err != nil { + t.Fatalf("failed to unmarshal decisionContext: %v", err) + } + if context.Surface != PermissionDecisionSurfaceSDK { + t.Fatalf("expected replaced surface %q, got %q", PermissionDecisionSurfaceSDK, context.Surface) + } +} + +func TestAttributedNoResultStillSuppressesResponse(t *testing.T) { + frame, sent := runPermissionExchange(t, func(PermissionRequest, PermissionInvocation) (rpc.PermissionDecision, error) { + return NewAttributedPermissionResult(&rpc.PermissionDecisionNoResult{}, sampleDecisionContext()), nil + }) + if sent { + t.Fatalf("expected no response to be sent for an attributed no-result decision, got frame: %s", frame) + } +} + +// A handler may dereference the wrapper and return it by value. The embedded +// interface promotes its methods to the value type, so the value form also +// satisfies rpc.PermissionDecision and must be unwrapped identically to the +// pointer form -- otherwise the wrapper itself is sent as result and the +// context is silently dropped. +func TestValueFormAttributedResultIsUnwrapped(t *testing.T) { + frame, sent := runPermissionExchange(t, func(PermissionRequest, PermissionInvocation) (rpc.PermissionDecision, error) { + return *NewAttributedPermissionResult(&rpc.PermissionDecisionApproveOnce{}, sampleDecisionContext()), nil + }) + if !sent { + t.Fatal("expected a permission response to be sent") + } + + params := paramsOf(t, frame) + + if _, ok := params["decisionContext"]; !ok { + t.Fatal("expected decisionContext to be forwarded for a value-form attributed result") + } + + var result map[string]json.RawMessage + if err := json.Unmarshal(params["result"], &result); err != nil { + t.Fatalf("failed to unmarshal result: %v", err) + } + if _, nested := result["decisionContext"]; nested { + t.Fatal("decisionContext must not be nested inside result") + } + if _, leaked := result["PermissionDecision"]; leaked { + t.Fatal("the wrapper leaked into result instead of being unwrapped") + } +} + +func TestAttributedResultReplacesContextOnValueForm(t *testing.T) { + first := sampleDecisionContext() + second := &rpc.PermissionDecisionContext{ + Outcome: PermissionDecisionOutcomePromptedUser, + Source: PermissionDecisionSourceHumanResponse, + Surface: PermissionDecisionSurfaceTui, + } + + valueForm := *NewAttributedPermissionResult(&rpc.PermissionDecisionApproveOnce{}, first) + replaced := NewAttributedPermissionResult(valueForm, second) + + if replaced.DecisionContext != second { + t.Fatal("expected the second context to replace the first") + } + if _, nested := replaced.PermissionDecision.(AttributedPermissionResult); nested { + t.Fatal("value-form attribution must be replaced, not nested") + } +} diff --git a/go/permissions.go b/go/permissions.go index 24b9cc7f1..f27f9b6e6 100644 --- a/go/permissions.go +++ b/go/permissions.go @@ -6,6 +6,62 @@ import ( "github.com/github/copilot-sdk/go/rpc" ) +// AttributedPermissionResult pairs a permission decision with the context +// describing how it was reached, so the runtime can attribute auto-approval +// telemetry to the responding surface. +// +// The embedded [rpc.PermissionDecision] carries the actual decision, while +// DecisionContext is informational only and never changes permission behavior. +// It satisfies [rpc.PermissionDecision] itself, so a [PermissionHandlerFunc] +// can return it wherever a plain decision is expected. Prefer constructing it +// through [NewAttributedPermissionResult] rather than by hand. +// +// Experimental: AttributedPermissionResult is part of an experimental API and +// may change or be removed. +type AttributedPermissionResult struct { + rpc.PermissionDecision + // DecisionContext describes how and where the decision was reached. When nil + // the SDK omits it from the wire, preserving legacy behavior. + DecisionContext *rpc.PermissionDecisionContext +} + +// NewAttributedPermissionResult pairs a permission decision with the context +// describing how it was reached, so the runtime can attribute auto-approval +// telemetry to the responding surface. +// +// The returned value satisfies [rpc.PermissionDecision], so a +// [PermissionHandlerFunc] can return it directly. Passing an already-attributed +// result replaces the previous context rather than nesting it. If result is a +// [rpc.PermissionDecisionNoResult] (attributed or not), the SDK still +// suppresses the response. +// +// Experimental: NewAttributedPermissionResult is part of an experimental API +// and may change or be removed. +func NewAttributedPermissionResult(result rpc.PermissionDecision, decisionContext *rpc.PermissionDecisionContext) *AttributedPermissionResult { + decision, _ := splitAttribution(result) + return &AttributedPermissionResult{ + PermissionDecision: decision, + DecisionContext: decisionContext, + } +} + +// splitAttribution separates an optionally attributed result into the bare +// decision and its context, returning a nil context when there is none. +// +// Both the pointer and value forms are matched: embedding an interface promotes +// its methods to the value type too, so an AttributedPermissionResult passed by +// value also satisfies [rpc.PermissionDecision] and must not slip through +// unwrapped. +func splitAttribution(result rpc.PermissionDecision) (rpc.PermissionDecision, *rpc.PermissionDecisionContext) { + switch attributed := result.(type) { + case *AttributedPermissionResult: + return attributed.PermissionDecision, attributed.DecisionContext + case AttributedPermissionResult: + return attributed.PermissionDecision, attributed.DecisionContext + } + return result, nil +} + // PermissionHandler provides pre-built OnPermissionRequest implementations. var PermissionHandler = struct { // ApproveAll approves permission requests when managed settings are disabled. diff --git a/go/session.go b/go/session.go index 99939de4a..600a4bbeb 100644 --- a/go/session.go +++ b/go/session.go @@ -1646,6 +1646,10 @@ func (s *Session) executePermissionAndRespond(requestID string, permissionReques }) return } + // Unwrap any attribution so decisionContext travels as a sibling of result, + // not nested inside it. The suppression and send logic below operates on the + // underlying decision. + decision, decisionContext := splitAttribution(decision) if _, ok := decision.(*rpc.PermissionDecisionNoResult); ok { return } @@ -1654,8 +1658,9 @@ func (s *Session) executePermissionAndRespond(requestID string, permissionReques } s.RPC.Permissions.HandlePendingPermissionRequest(context.Background(), &rpc.PermissionDecisionRequest{ - RequestID: requestID, - Result: decision, + RequestID: requestID, + Result: decision, + DecisionContext: decisionContext, }) } diff --git a/go/types.go b/go/types.go index c0b34586d..2241d2b5f 100644 --- a/go/types.go +++ b/go/types.go @@ -384,6 +384,47 @@ type PermissionInvocation struct { ManagedSettingsEnabled bool } +// PermissionDecisionContext describes how and where a permission decision was +// reached. Attach it to a decision with [NewAttributedPermissionResult] so the runtime +// can attribute auto-approval telemetry to the responding surface. It is +// informational only and never changes permission behavior. +// +// Experimental: PermissionDecisionContext is part of an experimental API and +// may change or be removed. +type PermissionDecisionContext = rpc.PermissionDecisionContext + +// PermissionDecisionOutcome describes the disposition of a permission request +// as observed by the responding client. +type PermissionDecisionOutcome = rpc.PermissionDecisionOutcome + +const ( + PermissionDecisionOutcomeAutoApproved = rpc.PermissionDecisionOutcomeAutoApproved + PermissionDecisionOutcomeAutopilotDenied = rpc.PermissionDecisionOutcomeAutopilotDenied + PermissionDecisionOutcomePromptedUser = rpc.PermissionDecisionOutcomePromptedUser +) + +// PermissionDecisionSource identifies the controlled reason or actor +// responsible for a permission response. +type PermissionDecisionSource = rpc.PermissionDecisionSource + +const ( + PermissionDecisionSourceHostPolicy = rpc.PermissionDecisionSourceHostPolicy + PermissionDecisionSourceHumanResponse = rpc.PermissionDecisionSourceHumanResponse + PermissionDecisionSourceJudgeRecommendation = rpc.PermissionDecisionSourceJudgeRecommendation + PermissionDecisionSourceUnattendedFallback = rpc.PermissionDecisionSourceUnattendedFallback +) + +// PermissionDecisionSurface identifies the client surface that submitted a +// permission response. +type PermissionDecisionSurface = rpc.PermissionDecisionSurface + +const ( + PermissionDecisionSurfaceCopilotApp = rpc.PermissionDecisionSurfaceCopilotApp + PermissionDecisionSurfacePromptMode = rpc.PermissionDecisionSurfacePromptMode + PermissionDecisionSurfaceSDK = rpc.PermissionDecisionSurfaceSDK + PermissionDecisionSurfaceTui = rpc.PermissionDecisionSurfaceTui +) + // MCPAuthWwwAuthenticateParams contains parsed parameters from an MCP server's WWW-Authenticate response. type MCPAuthWwwAuthenticateParams struct { ResourceMetadataURL *string `json:"resourceMetadataUrl,omitempty"` diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotSession.java b/java/sdk/src/main/java/com/github/copilot/CopilotSession.java index 4683fdf01..ca2adf462 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotSession.java @@ -1026,7 +1026,7 @@ private void executePermissionAndRespondAsync(String requestId, PermissionReques } getRpc().permissions.handlePendingPermissionRequest( new SessionPermissionsHandlePendingPermissionRequestParams(sessionId, requestId, result, - null)); + result.getDecisionContext())); } catch (Exception e) { LOG.log(Level.WARNING, "Error sending permission result for requestId=" + requestId, e); } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java index 2e5c60100..6546291cf 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/PermissionRequestResult.java @@ -6,8 +6,10 @@ import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.generated.rpc.PermissionDecisionContext; /** * Result of a permission request decision. @@ -42,6 +44,15 @@ public final class PermissionRequestResult { @JsonProperty("feedback") private String feedback; + /** + * Optional provenance describing how and where this decision was made. Never + * serialized inside the result — the SDK forwards it as a sibling of + * {@code result} so the runtime can attribute {@code auto_approval_decision} + * telemetry. + */ + @JsonIgnore + private PermissionDecisionContext decisionContext; + /** * Creates a result that approves this single request. * @@ -168,4 +179,35 @@ public PermissionRequestResult setFeedback(String feedback) { this.feedback = feedback; return this; } + + /** + * Gets the optional provenance describing how and where this decision was made. + *

+ * This value is never serialized inside the result JSON; the SDK forwards it as + * a sibling of {@code result} when responding to the runtime. + * + * @return the decision context, or {@code null} if none was attached + * @since 1.3.0 + */ + public PermissionDecisionContext getDecisionContext() { + return decisionContext; + } + + /** + * Sets provenance describing how and where this decision was made, so the + * runtime can attribute {@code auto_approval_decision} telemetry. + *

+ * Calling this method more than once replaces any previously set context. The + * context is never serialized inside the result; the SDK forwards it as a + * sibling of {@code result}. + * + * @param decisionContext + * the decision context, or {@code null} to attach none + * @return this result for method chaining + * @since 1.3.0 + */ + public PermissionRequestResult setDecisionContext(PermissionDecisionContext decisionContext) { + this.decisionContext = decisionContext; + return this; + } } diff --git a/java/sdk/src/test/java/com/github/copilot/rpc/PermissionRequestResultDecisionContextTest.java b/java/sdk/src/test/java/com/github/copilot/rpc/PermissionRequestResultDecisionContextTest.java new file mode 100644 index 000000000..395ad50ad --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/rpc/PermissionRequestResultDecisionContextTest.java @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.generated.rpc.PermissionDecisionContext; +import com.github.copilot.generated.rpc.PermissionDecisionOutcome; +import com.github.copilot.generated.rpc.PermissionDecisionSource; +import com.github.copilot.generated.rpc.PermissionDecisionSurface; +import com.github.copilot.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; +import org.junit.jupiter.api.Test; + +/** + * Verifies that {@link PermissionRequestResult} carries an optional + * {@link PermissionDecisionContext} as a sibling of {@code result} — never + * nested inside the serialized result — when the SDK forwards a permission + * response to the runtime. + */ +class PermissionRequestResultDecisionContextTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private static PermissionDecisionContext sampleContext() { + return new PermissionDecisionContext(PermissionDecisionOutcome.AUTO_APPROVED, + PermissionDecisionSource.HOST_POLICY, PermissionDecisionSurface.SDK); + } + + @Test + void setDecisionContextForwardsContextAsSiblingOfResult() throws Exception { + var result = PermissionRequestResult.approveOnce().setDecisionContext(sampleContext()); + var params = new SessionPermissionsHandlePendingPermissionRequestParams("session-1", "req-1", result, + result.getDecisionContext()); + + JsonNode json = MAPPER.valueToTree(params); + + assertTrue(json.has("decisionContext"), "decisionContext must be a top-level sibling of result"); + assertEquals("host_policy", json.get("decisionContext").get("source").asText()); + assertEquals("auto_approved", json.get("decisionContext").get("outcome").asText()); + assertEquals("sdk", json.get("decisionContext").get("surface").asText()); + assertFalse(json.get("result").has("decisionContext"), "decisionContext must NOT be nested inside result"); + } + + @Test + void withoutContextOmitsDecisionContextKey() throws Exception { + var result = PermissionRequestResult.approveOnce(); + assertNull(result.getDecisionContext()); + + var params = new SessionPermissionsHandlePendingPermissionRequestParams("session-1", "req-1", result, + result.getDecisionContext()); + + JsonNode json = MAPPER.valueToTree(params); + + // Generated params record is @JsonInclude(NON_NULL), so a null + // decisionContext is omitted entirely — byte-identical to legacy behavior. + assertFalse(json.has("decisionContext"), "decisionContext key must be absent when no context is supplied"); + } + + @Test + void setDecisionContextTwiceReplacesRatherThanNests() { + var first = sampleContext(); + var second = new PermissionDecisionContext(PermissionDecisionOutcome.PROMPTED_USER, + PermissionDecisionSource.HUMAN_RESPONSE, PermissionDecisionSurface.TUI); + + var result = PermissionRequestResult.approveOnce().setDecisionContext(first).setDecisionContext(second); + + assertSame(second, result.getDecisionContext(), "second setDecisionContext must replace the first, not nest"); + } + + @Test + void serializingResultWithContextDoesNotEmitContextInsideResult() throws Exception { + var result = PermissionRequestResult.approveOnce().setDecisionContext(sampleContext()); + + JsonNode resultJson = MAPPER.valueToTree(result); + + assertFalse(resultJson.has("decisionContext"), + "@JsonIgnore must keep decisionContext out of the serialized result"); + assertEquals(PermissionRequestResultKind.APPROVED.getValue(), resultJson.get("kind").asText()); + } + + @Test + void setDecisionContextAcceptsNullAsNoContext() { + var result = PermissionRequestResult.approveOnce().setDecisionContext(sampleContext()); + + result.setDecisionContext(null); + + assertNull(result.getDecisionContext(), "null must clear the context rather than throwing"); + } +} diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts index 622fd38b5..f91e351d3 100644 --- a/nodejs/src/index.ts +++ b/nodejs/src/index.ts @@ -27,6 +27,7 @@ export { export { defineTool, approveAll, + createAttributedPermissionResult, convertMcpCallToolResult, createSessionFsAdapter, CopilotRequestHandler, @@ -124,6 +125,11 @@ export type { PermissionRequestedData, PermissionRequestedEvent, PermissionRequestResult, + AttributedPermissionResult, + PermissionDecisionContext, + PermissionDecisionOutcome, + PermissionDecisionSource, + PermissionDecisionSurface, ProviderConfig, ProviderModelConfig, ProviderTokenArgs, diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 42e30d6f3..48e41483b 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -22,6 +22,7 @@ import type { import { type Canvas, CanvasError } from "./canvas.js"; import type { OpenCanvasInstance } from "./generated/rpc.js"; import { getTraceContext } from "./telemetry.js"; +import { isAttributedPermissionResult } from "./types.js"; import type { CommandHandler, AutoModeSwitchHandler, @@ -41,6 +42,7 @@ import type { McpAuthRequest, PermissionHandler, PermissionRequest, + PermissionRequestResult, ContextTier, ReasoningEffort, ReasoningSummary, @@ -1133,17 +1135,26 @@ export class CopilotSession { permissionRequest: PermissionRequest ): Promise { try { - const result = await this.permissionHandler!(permissionRequest, { + const handlerResult = await this.permissionHandler!(permissionRequest, { sessionId: this.sessionId, managedSettingsEnabled: this.managedSettingsEnabled, }); + const isAttributed = isAttributedPermissionResult(handlerResult); + const result: PermissionRequestResult = isAttributed + ? handlerResult.result + : handlerResult; + const decisionContext = isAttributed ? handlerResult.decisionContext : undefined; if (result.kind === "no-result") { return; } if (this.disconnected) { return; } - await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result }); + await this.rpc.permissions.handlePendingPermissionRequest( + decisionContext === undefined + ? { requestId, result } + : { requestId, result, decisionContext } + ); } catch (error) { if (this.disconnected) { return; diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 8149c36da..06d6bf7eb 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -53,6 +53,12 @@ export type { SessionFsSqliteStatement } from "./sessionFsProvider.js"; export type { SessionFsSqliteTransactionErrorClass } from "./sessionFsProvider.js"; export { SessionFsSqliteTransactionFailure } from "./sessionFsProvider.js"; export type { LlmInferenceHeaders } from "./generated/rpc.js"; +export type { + PermissionDecisionContext, + PermissionDecisionOutcome, + PermissionDecisionSource, + PermissionDecisionSurface, +} from "./generated/rpc.js"; export type { CopilotRequestContext } from "./copilotRequestHandler.js"; export { CopilotRequestHandler, @@ -1120,7 +1126,7 @@ export type SystemMessageConfig = | SystemMessageReplaceConfig | SystemMessageCustomizeConfig; -import type { PermissionDecisionRequest } from "./generated/rpc.js"; +import type { PermissionDecisionRequest, PermissionDecisionContext } from "./generated/rpc.js"; /** * Permission request types from the server. This is the generated @@ -1156,10 +1162,51 @@ export type PermissionRequestedEvent = Omit Promise | PermissionRequestResult; +) => + | Promise + | PermissionRequestResult + | AttributedPermissionResult; /** * Approves permission requests when managed settings are disabled. diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts index 49ec169c4..841aa599d 100644 --- a/nodejs/test/client.test.ts +++ b/nodejs/test/client.test.ts @@ -7,6 +7,7 @@ import { join, resolve } from "node:path"; import { describe, expect, it, onTestFinished, vi } from "vitest"; import { approveAll, + createAttributedPermissionResult, CopilotClient, createCanvas, RuntimeConnection, @@ -128,6 +129,91 @@ describe("CopilotClient", () => { expect(spy).not.toHaveBeenCalled(); }); + it("forwards decisionContext as a top-level sibling of result", async () => { + const session = new CopilotSession("session-1", {} as any); + const decisionContext = { + outcome: "auto_approved" as const, + source: "host_policy" as const, + surface: "sdk" as const, + }; + session.registerPermissionHandler(() => + createAttributedPermissionResult({ kind: "approve-once" }, decisionContext) + ); + const spy = vi + .spyOn(session.rpc.permissions, "handlePendingPermissionRequest") + .mockResolvedValue({ kind: "approve-once" } as any); + + await (session as any)._executePermissionAndRespond("request-1", { kind: "write" }); + + expect(spy).toHaveBeenCalledOnce(); + const params = spy.mock.calls[0][0] as any; + expect(params).toEqual({ + requestId: "request-1", + result: { kind: "approve-once" }, + decisionContext, + }); + // decisionContext is a sibling of result, never nested inside it. + expect(params.result.decisionContext).toBeUndefined(); + }); + + it("emits exactly requestId and result with no decisionContext key when unattributed", async () => { + const session = new CopilotSession("session-1", {} as any); + session.registerPermissionHandler(() => ({ kind: "approve-once" })); + const spy = vi + .spyOn(session.rpc.permissions, "handlePendingPermissionRequest") + .mockResolvedValue({ kind: "approve-once" } as any); + + await (session as any)._executePermissionAndRespond("request-1", { kind: "write" }); + + expect(spy).toHaveBeenCalledOnce(); + const params = spy.mock.calls[0][0] as any; + expect(params).toEqual({ requestId: "request-1", result: { kind: "approve-once" } }); + expect(Object.keys(params).sort()).toEqual(["requestId", "result"]); + expect("decisionContext" in params).toBe(false); + }); + + it("does not respond when a no-result decision is wrapped with a context", async () => { + const session = new CopilotSession("session-1", {} as any); + const decisionContext = { + outcome: "auto_approved" as const, + source: "host_policy" as const, + surface: "sdk" as const, + }; + session.registerPermissionHandler(() => + createAttributedPermissionResult({ kind: "no-result" }, decisionContext) + ); + const spy = vi.spyOn(session.rpc.permissions, "handlePendingPermissionRequest"); + + await (session as any)._executePermissionAndRespond("request-1", { kind: "write" }); + + expect(spy).not.toHaveBeenCalled(); + }); + + it("replaces the context when applied twice", () => { + const first = { + outcome: "auto_approved" as const, + source: "judge_recommendation" as const, + surface: "sdk" as const, + }; + const second = { + outcome: "prompted_user" as const, + source: "human_response" as const, + surface: "tui" as const, + }; + + const once = createAttributedPermissionResult({ kind: "approve-once" }, first); + const twice = createAttributedPermissionResult(once, second); + + expect(twice).toEqual({ + kind: "attributed", + result: { kind: "approve-once" }, + decisionContext: second, + }); + // The result stays unwrapped rather than nesting an AttributedPermissionResult. + expect((twice.result as any).result).toBeUndefined(); + expect((twice.result as any).decisionContext).toBeUndefined(); + }); + it("responds to MCP OAuth requests with host token data", async () => { const sendRequest = vi.fn(async () => ({ success: true })); let observedRequest: any; diff --git a/nodejs/test/e2e/permissions.e2e.test.ts b/nodejs/test/e2e/permissions.e2e.test.ts index e7c26a293..b7fa6087a 100644 --- a/nodejs/test/e2e/permissions.e2e.test.ts +++ b/nodejs/test/e2e/permissions.e2e.test.ts @@ -5,14 +5,15 @@ import { realpathSync } from "fs"; import { mkdir, readFile, writeFile } from "fs/promises"; import { join } from "path"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { z } from "zod"; import type { + PermissionDecisionContext, PermissionRequest, PermissionRequestResult, ToolResultObject, } from "../../src/index.js"; -import { approveAll, defineTool } from "../../src/index.js"; +import { approveAll, defineTool, createAttributedPermissionResult } from "../../src/index.js"; import { createSdkTestContext, isInProcessTransport } from "./harness/sdkTestContext.js"; import { getFinalAssistantMessage, getNextEventOfType } from "./harness/sdkTestHelper.js"; @@ -90,6 +91,61 @@ describe("Permission callbacks", async () => { await session.disconnect(); }); + it("should honor a decision annotated with decisionContext", async () => { + // End-to-end proof that decisionContext survives the real permission flow. + // The runtime only emits its auto_approval_decision telemetry when its own + // auto-approval judge metadata is also present (feature-flagged and model + // backed), so that event is not observable here. Instead we assert the exact + // params handed to the CLI: decisionContext must be a top-level sibling of + // `result`, never nested inside it. The CLI tolerates a nested key silently, + // so asserting the params shape is what actually gives this test teeth. + const decisionContext: PermissionDecisionContext = { + outcome: "prompted_user", + source: "human_response", + surface: "sdk", + }; + + const session = await client.createSession({ + onPermissionRequest: () => + createAttributedPermissionResult({ kind: "reject" }, decisionContext), + }); + + // Spies preserve the original implementation, so the decision still reaches + // the CLI and the assertions below observe a real, honored round-trip. + const respondSpy = vi.spyOn(session.rpc.permissions, "handlePendingPermissionRequest"); + + let userRejectedToolCall = false; + session.on((event) => { + if ( + event.type === "tool.execution_complete" && + !event.data.success && + event.data.error?.message.toLowerCase().includes("user rejected") + ) { + userRejectedToolCall = true; + } + }); + + const originalContent = "protected content"; + const testFile = join(workDir, "protected.txt"); + await writeFile(testFile, originalContent); + + await session.sendAndWait({ + prompt: "Edit protected.txt and replace 'protected' with 'hacked'.", + }); + + // The decision was applied by the CLI, not merely sent. + expect(userRejectedToolCall).toBe(true); + expect(await readFile(testFile, "utf-8")).toBe(originalContent); + + expect(respondSpy).toHaveBeenCalled(); + const params = respondSpy.mock.calls[0]![0]; + expect(params.decisionContext).toEqual(decisionContext); + expect(params.result).toEqual({ kind: "reject" }); + expect(Object.keys(params).sort()).toEqual(["decisionContext", "requestId", "result"]); + + await session.disconnect(); + }); + it("should deny tool operations when handler explicitly denies", async () => { let permissionDenied = false; diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index a7366db54..f7a71ebe9 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -87,6 +87,10 @@ GitHubTelemetryNotification, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, + PermissionDecisionContext, + PermissionDecisionOutcome, + PermissionDecisionSource, + PermissionDecisionSurface, ) from .generated.session_events import ( PermissionRequest, @@ -97,6 +101,7 @@ AgentStopHandler, AgentStopHookInput, AgentStopHookOutput, + AttributedPermissionResult, AutoModeSwitchHandler, AutoModeSwitchRequest, AutoModeSwitchResponse, @@ -178,6 +183,7 @@ UserPromptTransformedHandler, UserPromptTransformedHookInput, UserPromptTransformedHookOutput, + create_attributed_permission_result, ) from .session_fs_provider import ( SessionFsFileInfo, @@ -209,6 +215,7 @@ "AgentStopHandler", "AgentStopHookInput", "AgentStopHookOutput", + "AttributedPermissionResult", "AutoModeSwitchHandler", "AutoModeSwitchRequest", "AutoModeSwitchResponse", @@ -295,6 +302,10 @@ "PermissionNoResult", "PermissionRequest", "PermissionRequestResult", + "PermissionDecisionContext", + "PermissionDecisionOutcome", + "PermissionDecisionSource", + "PermissionDecisionSurface", "PingResponse", "PostToolUseHandler", "PostToolUseFailureHandler", @@ -373,6 +384,7 @@ "UserPromptTransformedHookInput", "UserPromptTransformedHookOutput", "convert_mcp_call_tool_result", + "create_attributed_permission_result", "create_session_fs_adapter", "define_tool", ] diff --git a/python/copilot/session.py b/python/copilot/session.py index 92c24bdd8..2399ab36e 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -44,6 +44,7 @@ ModelSwitchToRequest, PermissionDecision, PermissionDecisionApproveOnce, + PermissionDecisionContext, PermissionDecisionRequest, PermissionDecisionUserNotAvailable, ProviderTokenAcquireRequest, @@ -367,6 +368,44 @@ class PermissionNoResult: PermissionRequestResult = PermissionDecision | PermissionNoResult +@dataclass +class AttributedPermissionResult: + """A permission result annotated with the context describing how it was reached. + + The Copilot runtime emits an ``auto_approval_decision`` telemetry event only + when a client supplies an explicit :class:`PermissionDecisionContext` alongside + its permission reply. Wrapping a :data:`PermissionRequestResult` with this class + forwards that context to the runtime as a sibling of the decision on the wire. + + The context is informational only — it never changes permission behavior. Build + instances via :func:`create_attributed_permission_result` rather than constructing + directly, so re-attributing an already-wrapped result replaces the context instead + of nesting. + """ + + result: PermissionRequestResult + """The underlying permission decision (or :class:`PermissionNoResult`).""" + + decision_context: PermissionDecisionContext + """Context describing how and where the decision was reached.""" + + +def create_attributed_permission_result( + result: PermissionRequestResult | AttributedPermissionResult, + decision_context: PermissionDecisionContext, +) -> AttributedPermissionResult: + """Annotate a permission result with the context describing how it was reached. + + Returns an :class:`AttributedPermissionResult` carrying ``result`` and + ``decision_context`` as siblings. If ``result`` is already an + :class:`AttributedPermissionResult`, its underlying decision is preserved and the + context is *replaced* — attribution never nests. + """ + if isinstance(result, AttributedPermissionResult): + result = result.result + return AttributedPermissionResult(result=result, decision_context=decision_context) + + class PermissionInvocation(TypedDict, total=False): session_id: Required[str] managed_settings_enabled: NotRequired[bool] @@ -374,7 +413,9 @@ class PermissionInvocation(TypedDict, total=False): _PermissionHandlerFn = Callable[ [PermissionRequest, PermissionInvocation], - PermissionRequestResult | Awaitable[PermissionRequestResult], + PermissionRequestResult + | AttributedPermissionResult + | Awaitable[PermissionRequestResult | AttributedPermissionResult], ] @@ -2174,7 +2215,11 @@ async def _execute_permission_and_respond( request_id=request_id, ) - result = cast(PermissionRequestResult, result) + result = cast("PermissionRequestResult | AttributedPermissionResult", result) + decision_context: PermissionDecisionContext | None = None + if isinstance(result, AttributedPermissionResult): + decision_context = result.decision_context + result = result.result if isinstance(result, PermissionNoResult): return @@ -2183,6 +2228,7 @@ async def _execute_permission_and_respond( PermissionDecisionRequest( request_id=request_id, result=result, + decision_context=decision_context, ) ) log_timing( diff --git a/python/test_permission_decision_context.py b/python/test_permission_decision_context.py new file mode 100644 index 000000000..2b013942d --- /dev/null +++ b/python/test_permission_decision_context.py @@ -0,0 +1,99 @@ +from unittest.mock import AsyncMock, MagicMock + +from copilot.rpc import ( + PermissionDecisionApproveOnce, + PermissionDecisionContext, + PermissionDecisionOutcome, + PermissionDecisionSource, + PermissionDecisionSurface, +) +from copilot.session import ( + AttributedPermissionResult, + CopilotSession, + PermissionNoResult, + create_attributed_permission_result, +) +from copilot.session_events import PermissionRequestRead + + +def _context() -> PermissionDecisionContext: + return PermissionDecisionContext( + outcome=PermissionDecisionOutcome.AUTO_APPROVED, + source=PermissionDecisionSource.HOST_POLICY, + surface=PermissionDecisionSurface.SDK, + ) + + +def _session_with_captured_rpc() -> tuple[CopilotSession, AsyncMock]: + session = CopilotSession("session-1", client=None) + handle = AsyncMock() + rpc = MagicMock() + rpc.permissions.handle_pending_permission_request = handle + session._rpc = rpc + return session, handle + + +async def test_decision_context_serialized_as_sibling_of_result() -> None: + session, handle = _session_with_captured_rpc() + request = PermissionRequestRead(intention="Read", path="/workspace/file.txt") + + def handler(_request, _invocation): + return create_attributed_permission_result(PermissionDecisionApproveOnce(), _context()) + + await session._execute_permission_and_respond("permission-1", request, handler) + + handle.assert_awaited_once() + sent = handle.await_args.args[0] + params = sent.to_dict() + + assert params["decisionContext"] == { + "outcome": "auto_approved", + "source": "host_policy", + "surface": "sdk", + } + assert "decisionContext" not in params["result"] + assert params["result"]["kind"] == "approve-once" + + +async def test_no_context_omits_decision_context_key() -> None: + session, handle = _session_with_captured_rpc() + request = PermissionRequestRead(intention="Read", path="/workspace/file.txt") + + def handler(_request, _invocation): + return PermissionDecisionApproveOnce() + + await session._execute_permission_and_respond("permission-1", request, handler) + + handle.assert_awaited_once() + params = handle.await_args.args[0].to_dict() + + assert "decisionContext" not in params + assert params["result"]["kind"] == "approve-once" + + +def test_attributed_result_replaces_rather_than_nests() -> None: + first = PermissionDecisionContext( + outcome=PermissionDecisionOutcome.PROMPTED_USER, + source=PermissionDecisionSource.HUMAN_RESPONSE, + surface=PermissionDecisionSurface.TUI, + ) + second = _context() + + once_wrapped = create_attributed_permission_result(PermissionDecisionApproveOnce(), first) + twice_wrapped = create_attributed_permission_result(once_wrapped, second) + + assert isinstance(twice_wrapped, AttributedPermissionResult) + assert isinstance(twice_wrapped.result, PermissionDecisionApproveOnce) + assert twice_wrapped.decision_context is second + + +async def test_no_result_with_context_still_suppresses_response() -> None: + session, handle = _session_with_captured_rpc() + request = PermissionRequestRead(intention="Read", path="/workspace/file.txt") + + def handler(_request, _invocation): + return create_attributed_permission_result(PermissionNoResult(), _context()) + + await session._execute_permission_and_respond("permission-1", request, handler) + + handle.assert_not_awaited() diff --git a/rust/src/handler.rs b/rust/src/handler.rs index 77edf919c..f1f0d9566 100644 --- a/rust/src/handler.rs +++ b/rust/src/handler.rs @@ -22,7 +22,7 @@ use crate::generated::api_types::{ McpOauthPendingRequestResponse, McpOauthPendingRequestResponseCancelled, McpOauthPendingRequestResponseCancelledKind, McpOauthPendingRequestResponseToken, McpOauthPendingRequestResponseTokenKind, PermissionDecision, PermissionDecisionApproveOnce, - PermissionDecisionReject, PermissionDecisionUserNotAvailable, + PermissionDecisionContext, PermissionDecisionReject, PermissionDecisionUserNotAvailable, }; use crate::session_events::{ McpOauthRequestReason, McpOauthRequiredStaticClientConfig, McpOauthWWWAuthenticateParams, @@ -35,13 +35,30 @@ use crate::types::{ /// Decision returned by a [`PermissionHandler`]. /// /// Either a concrete wire-level [`PermissionDecision`] (approve, reject, -/// approve-for-session, approve-permanently, user-not-available, …) or -/// [`PermissionResult::NoResult`], which tells the SDK to suppress its -/// response so another connected client can answer instead. +/// approve-for-session, approve-permanently, user-not-available, …) with +/// optional telemetry context, or [`PermissionResult::NoResult`], which tells +/// the SDK to suppress its response so another connected client can answer +/// instead. +/// +/// ``` +/// use github_copilot_sdk::handler::PermissionResult; +/// +/// fn is_decision(result: PermissionResult) -> bool { +/// match result { +/// PermissionResult::Decision { .. } => true, +/// PermissionResult::NoResult => false, +/// } +/// } +/// ``` #[derive(Debug, Clone)] pub enum PermissionResult { /// Send a permission decision on the wire. - Decision(PermissionDecision), + Decision { + /// The decision to send. + decision: PermissionDecision, + /// Optional context describing how and where the decision was reached. + context: Option, + }, /// Decline to respond to this request, allowing another connected /// client to answer instead. The SDK suppresses the response. NoResult, @@ -50,24 +67,31 @@ pub enum PermissionResult { impl PermissionResult { /// Approve this single request. pub fn approve_once() -> Self { - Self::Decision(PermissionDecision::ApproveOnce( - PermissionDecisionApproveOnce::default(), - )) + Self::Decision { + decision: PermissionDecision::ApproveOnce(PermissionDecisionApproveOnce::default()), + context: None, + } } /// Reject the request, optionally forwarding feedback to the LLM. pub fn reject(feedback: impl Into>) -> Self { - Self::Decision(PermissionDecision::Reject(PermissionDecisionReject { - feedback: feedback.into(), - ..Default::default() - })) + Self::Decision { + decision: PermissionDecision::Reject(PermissionDecisionReject { + feedback: feedback.into(), + ..Default::default() + }), + context: None, + } } /// Deny because no user is available to confirm. pub fn user_not_available() -> Self { - Self::Decision(PermissionDecision::UserNotAvailable( - PermissionDecisionUserNotAvailable::default(), - )) + Self::Decision { + decision: PermissionDecision::UserNotAvailable( + PermissionDecisionUserNotAvailable::default(), + ), + context: None, + } } /// Decline to respond, allowing another connected client to answer @@ -75,11 +99,42 @@ impl PermissionResult { pub fn no_result() -> Self { Self::NoResult } + + /// Attach provenance describing how and where this decision was made, + /// so the runtime can attribute auto-approval telemetry. + /// + /// It is a no-op on [`PermissionResult::NoResult`]. + /// + /// ```rust,no_run + /// # use github_copilot_sdk::handler::PermissionResult; + /// # use github_copilot_sdk::{ + /// # PermissionDecisionContext, PermissionDecisionOutcome, PermissionDecisionSource, + /// # PermissionDecisionSurface, + /// # }; + /// + /// let result = PermissionResult::approve_once().with_context(PermissionDecisionContext { + /// outcome: PermissionDecisionOutcome::AutoApproved, + /// source: PermissionDecisionSource::HostPolicy, + /// surface: PermissionDecisionSurface::Sdk, + /// }); + /// ``` + pub fn with_context(self, context: PermissionDecisionContext) -> Self { + match self { + Self::Decision { decision, .. } => Self::Decision { + decision, + context: Some(context), + }, + Self::NoResult => Self::NoResult, + } + } } impl From for PermissionResult { fn from(value: PermissionDecision) -> Self { - Self::Decision(value) + Self::Decision { + decision: value, + context: None, + } } } @@ -338,7 +393,10 @@ mod tests { .await; assert!(matches!( result, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -356,7 +414,10 @@ mod tests { .await; assert!(matches!( result, - PermissionResult::Decision(PermissionDecision::UserNotAvailable(_)) + PermissionResult::Decision { + decision: PermissionDecision::UserNotAvailable(_), + .. + } )); } @@ -386,7 +447,10 @@ mod tests { .await; assert!(matches!( result, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); } diff --git a/rust/src/permission.rs b/rust/src/permission.rs index e353ce315..57c078570 100644 --- a/rust/src/permission.rs +++ b/rust/src/permission.rs @@ -97,8 +97,7 @@ pub(crate) fn resolve_handler( ) -> Option> { match (handler, policy) { (_, Some(policy)) => Some(Arc::new(PolicyHandler { policy })), - (Some(h), None) => Some(h), - (None, None) => None, + (handler, None) => handler, } } @@ -152,7 +151,10 @@ mod tests { assert!(matches!( h.handle(SessionId::from("s"), RequestId::new("1"), data()) .await, - PermissionResult::Decision(crate::types::PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -164,7 +166,10 @@ mod tests { assert!(matches!( h.handle(SessionId::from("s"), RequestId::new("1"), request) .await, - PermissionResult::Decision(crate::types::PermissionDecision::UserNotAvailable(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::UserNotAvailable(_), + .. + } )); } @@ -174,7 +179,10 @@ mod tests { assert!(matches!( h.handle(SessionId::from("s"), RequestId::new("1"), data()) .await, - PermissionResult::Decision(crate::types::PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::Reject(_), + .. + } )); } @@ -184,7 +192,10 @@ mod tests { assert!(matches!( h.handle(SessionId::from("s"), RequestId::new("1"), data()) .await, - PermissionResult::Decision(crate::types::PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::Reject(_), + .. + } )); } @@ -208,7 +219,10 @@ mod tests { assert!(matches!( h.handle(SessionId::from("s"), RequestId::new("1"), request) .await, - PermissionResult::Decision(crate::types::PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::Reject(_), + .. + } )); } @@ -233,7 +247,10 @@ mod tests { resolved .handle(SessionId::from("s"), RequestId::new("1"), data()) .await, - PermissionResult::Decision(crate::types::PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::Reject(_), + .. + } )); } @@ -256,7 +273,10 @@ mod tests { resolved .handle(SessionId::from("s"), RequestId::new("1"), data()) .await, - PermissionResult::Decision(crate::types::PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: crate::types::PermissionDecision::ApproveOnce(_), + .. + } )); } diff --git a/rust/src/session.rs b/rust/src/session.rs index c6c806b1c..99e793015 100644 --- a/rust/src/session.rs +++ b/rust/src/session.rs @@ -12,8 +12,8 @@ use tracing::{Instrument, warn}; use crate::canvas::CanvasHandler; use crate::generated::api_types::{ - LogRequest, ModelSwitchToRequest, OpenCanvasInstance, RegisterEventInterestParams, - ToolsGetCurrentMetadataResult, rpc_methods, + LogRequest, ModelSwitchToRequest, OpenCanvasInstance, PermissionDecisionRequest, + RegisterEventInterestParams, ToolsGetCurrentMetadataResult, rpc_methods, }; use crate::generated::session_events::{ CommandExecuteData, ElicitationRequestedData, ExternalToolRequestedData, McpOauthRequiredData, @@ -1573,17 +1573,31 @@ fn permission_request_data( } } -/// Map a [`PermissionResult`] to the `result` payload sent back to the -/// server via `session.permissions.handlePendingPermissionRequest`. +/// Build the full `session.permissions.handlePendingPermissionRequest` +/// params for a permission result. +/// +/// `decisionContext` is a sibling of `result` and is only present when the +/// handler attributed the decision — omitting it preserves legacy behavior. /// /// Returns `None` when the SDK must not send a response. -fn notification_permission_payload(result: &PermissionResult) -> Option { - match result { - PermissionResult::NoResult => None, - PermissionResult::Decision(decision) => Some( - serde_json::to_value(decision).expect("serializing permission decision should succeed"), - ), - } +fn permission_response_params( + session_id: &SessionId, + request_id: &RequestId, + result: &PermissionResult, +) -> Option { + let (decision, decision_context) = match result { + PermissionResult::Decision { decision, context } => (decision, context.clone()), + PermissionResult::NoResult => return None, + }; + let mut params = serde_json::to_value(PermissionDecisionRequest { + decision_context, + request_id: request_id.clone(), + result: decision.clone(), + }) + .expect("serializing permission response should succeed"); + params["sessionId"] = + serde_json::to_value(session_id).expect("serializing session ID should succeed"); + Some(params) } async fn register_mcp_auth_interest(client: &Client, session_id: &SessionId) -> Result<(), Error> { @@ -1779,7 +1793,8 @@ async fn handle_notification( request_id = %request_id, "PermissionHandler::handle dispatch" ); - let Some(result_value) = notification_permission_payload(&result) else { + let Some(params) = permission_response_params(&sid, &request_id, &result) + else { // Handler returned Deferred / NoResult — it will // call handlePendingPermissionRequest itself (or // leave the request unanswered). @@ -1788,12 +1803,8 @@ async fn handle_notification( let rpc_start = Instant::now(); let _ = client .call( - "session.permissions.handlePendingPermissionRequest", - Some(serde_json::json!({ - "sessionId": sid, - "requestId": request_id, - "result": result_value, - })), + rpc_methods::SESSION_PERMISSIONS_HANDLEPENDINGPERMISSIONREQUEST, + Some(params), ) .await; tracing::debug!( @@ -2563,8 +2574,12 @@ fn inject_transform_sections_resume( mod tests { use serde_json::json; - use super::{has_managed_settings, notification_permission_payload, permission_request_data}; + use super::{has_managed_settings, permission_request_data, permission_response_params}; use crate::handler::PermissionResult; + use crate::types::{ + PermissionDecisionContext, PermissionDecisionOutcome, PermissionDecisionSource, + PermissionDecisionSurface, RequestId, SessionId, + }; #[test] fn direct_injection_enables_managed_safeguards() { @@ -2573,28 +2588,113 @@ mod tests { assert!(!has_managed_settings(None, None)); } + fn attribution_context() -> PermissionDecisionContext { + PermissionDecisionContext { + outcome: PermissionDecisionOutcome::AutoApproved, + source: PermissionDecisionSource::JudgeRecommendation, + surface: PermissionDecisionSurface::CopilotApp, + } + } + #[test] - fn notification_payload_suppresses_no_result() { - assert!(notification_permission_payload(&PermissionResult::NoResult).is_none()); + fn response_params_omit_decision_context_without_attribution() { + for (result, expected) in [ + ( + PermissionResult::approve_once(), + json!({ "kind": "approve-once" }), + ), + (PermissionResult::reject(None), json!({ "kind": "reject" })), + ( + PermissionResult::reject(Some("bad".to_string())), + json!({ "kind": "reject", "feedback": "bad" }), + ), + ( + PermissionResult::user_not_available(), + json!({ "kind": "user-not-available" }), + ), + ] { + let params = permission_response_params( + &SessionId::from("session-1"), + &RequestId::from("permission-1"), + &result, + ) + .unwrap(); + assert_eq!( + params, + json!({ + "sessionId": "session-1", + "requestId": "permission-1", + "result": expected, + }) + ); + } } #[test] - fn notification_payload_serializes_decisions() { - assert_eq!( - notification_permission_payload(&PermissionResult::approve_once()), - Some(json!({ "kind": "approve-once" })) - ); + fn response_params_forward_decision_context_alongside_result() { + let params = permission_response_params( + &SessionId::from("session-1"), + &RequestId::from("permission-1"), + &PermissionResult::approve_once().with_context(attribution_context()), + ) + .unwrap(); assert_eq!( - notification_permission_payload(&PermissionResult::reject(None)), - Some(json!({ "kind": "reject" })) + params, + json!({ + "sessionId": "session-1", + "requestId": "permission-1", + "result": { "kind": "approve-once" }, + "decisionContext": { + "outcome": "auto_approved", + "source": "judge_recommendation", + "surface": "copilot_app", + }, + }) ); - assert_eq!( - notification_permission_payload(&PermissionResult::reject(Some("bad".to_string()))), - Some(json!({ "kind": "reject", "feedback": "bad" })) + // The context is a sibling of `result`, never nested inside it. + assert!(params["result"].get("decisionContext").is_none()); + } + + #[test] + fn response_params_suppressed_for_no_result() { + assert!( + permission_response_params( + &SessionId::from("session-1"), + &RequestId::from("permission-1"), + &PermissionResult::NoResult, + ) + .is_none() ); + } + + #[test] + fn with_context_is_a_no_op_on_no_result() { + let result = PermissionResult::no_result().with_context(attribution_context()); + assert!(matches!(result, PermissionResult::NoResult)); + } + + #[test] + fn with_context_replaces_rather_than_nests() { + let result = PermissionResult::approve_once() + .with_context(attribution_context()) + .with_context(PermissionDecisionContext { + outcome: PermissionDecisionOutcome::PromptedUser, + source: PermissionDecisionSource::HumanResponse, + surface: PermissionDecisionSurface::Sdk, + }); + let params = permission_response_params( + &SessionId::from("session-1"), + &RequestId::from("permission-1"), + &result, + ) + .unwrap(); assert_eq!( - notification_permission_payload(&PermissionResult::user_not_available()), - Some(json!({ "kind": "user-not-available" })) + params["decisionContext"], + json!({ + "outcome": "prompted_user", + "source": "human_response", + "surface": "sdk", + }) ); } diff --git a/rust/src/types.rs b/rust/src/types.rs index 1946ded08..392e0f840 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -5773,7 +5773,9 @@ pub use crate::generated::api_types::{ Model, ModelBilling, ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, ModelCapabilities, ModelCapabilitiesLimits, ModelCapabilitiesLimitsVision, ModelCapabilitiesSupports, ModelList, ModelPolicy, PermissionDecision, - PermissionDecisionApproveOnce, PermissionDecisionReject, PermissionDecisionUserNotAvailable, + PermissionDecisionApproveOnce, PermissionDecisionContext, PermissionDecisionOutcome, + PermissionDecisionReject, PermissionDecisionSource, PermissionDecisionSurface, + PermissionDecisionUserNotAvailable, }; /// Permission categories the CLI may request approval for. @@ -7605,7 +7607,10 @@ mod permission_builder_tests { let h = resolve_create(cfg).expect("policy + handler yields handler"); assert!(matches!( dispatch(&h).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -7615,7 +7620,10 @@ mod permission_builder_tests { let h = resolve_create(cfg).expect("policy alone yields handler"); assert!(matches!( dispatch(&h).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -7633,11 +7641,17 @@ mod permission_builder_tests { let hb = resolve_create(b).unwrap(); assert!(matches!( dispatch(&ha).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); assert!(matches!( dispatch(&hb).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -7653,11 +7667,17 @@ mod permission_builder_tests { let hb = resolve_create(b).unwrap(); assert!(matches!( dispatch(&ha).await, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); assert!(matches!( dispatch(&hb).await, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); } @@ -7669,7 +7689,10 @@ mod permission_builder_tests { let h = resolve_create(cfg).unwrap(); assert!(matches!( dispatch(&h).await, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); } @@ -7688,11 +7711,17 @@ mod permission_builder_tests { let hb = resolve_create(b).unwrap(); assert!(matches!( dispatch(&ha).await, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); assert!(matches!( dispatch(&hb).await, - PermissionResult::Decision(PermissionDecision::Reject(_)) + PermissionResult::Decision { + decision: PermissionDecision::Reject(_), + .. + } )); } @@ -7704,7 +7733,10 @@ mod permission_builder_tests { let h = resolve_resume(cfg).unwrap(); assert!(matches!( dispatch(&h).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } @@ -7720,11 +7752,17 @@ mod permission_builder_tests { let hb = resolve_resume(b).unwrap(); assert!(matches!( dispatch(&ha).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); assert!(matches!( dispatch(&hb).await, - PermissionResult::Decision(PermissionDecision::ApproveOnce(_)) + PermissionResult::Decision { + decision: PermissionDecision::ApproveOnce(_), + .. + } )); } diff --git a/rust/tests/e2e/permissions.rs b/rust/tests/e2e/permissions.rs index 8f594841f..65b37928d 100644 --- a/rust/tests/e2e/permissions.rs +++ b/rust/tests/e2e/permissions.rs @@ -50,9 +50,10 @@ async fn should_handle_permission_handler_errors_gracefully() { assert!(matches!( result, - PermissionResult::Decision( - github_copilot_sdk::types::PermissionDecision::UserNotAvailable(_) - ) + PermissionResult::Decision { + decision: github_copilot_sdk::types::PermissionDecision::UserNotAvailable(_), + .. + } )); } diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs index 83a8489f2..231a8f91e 100644 --- a/rust/tests/session_test.rs +++ b/rust/tests/session_test.rs @@ -10,7 +10,7 @@ use github_copilot_sdk::canvas::{CanvasDeclaration, CanvasHandler, CanvasResult} use github_copilot_sdk::handler::{ ApproveAllHandler, AutoModeSwitchHandler, AutoModeSwitchResponse, ElicitationHandler, ExitPlanModeHandler, ExitPlanModeResult, McpAuthHandler, McpAuthRequest, McpAuthResult, - UserInputHandler, UserInputResponse, + PermissionHandler, PermissionResult, UserInputHandler, UserInputResponse, }; use github_copilot_sdk::rpc::{ CanvasProviderInvokeActionRequest, CanvasProviderOpenRequest, CanvasProviderOpenResult, @@ -24,8 +24,9 @@ use github_copilot_sdk::types::{ CanvasProviderIdentity, CloudSessionOptions, CloudSessionRepository, CommandContext, CommandDefinition, CommandHandler, DeliveryMode, DisableBypassPermissionsMode, ElicitationRequest, ElicitationResult, ExitPlanModeData, ExtensionInfo, ManagedSettings, - ManagedSettingsPermissions, MessageOptions, RequestId, SessionConfig, SessionId, - SetModelOptions, Tool, ToolInvocation, ToolResult, + ManagedSettingsPermissions, MessageOptions, PermissionDecisionContext, + PermissionDecisionOutcome, PermissionDecisionSource, PermissionDecisionSurface, RequestId, + SessionConfig, SessionId, SetModelOptions, Tool, ToolInvocation, ToolResult, }; use github_copilot_sdk::{Client, ContextTier, ErrorKind, ProtocolErrorKind, tool}; use serde_json::Value; @@ -38,6 +39,24 @@ struct TestCanvasHandler; struct CancelMcpAuthHandler; +struct ContextualApproveHandler; + +#[async_trait] +impl PermissionHandler for ContextualApproveHandler { + async fn handle( + &self, + _session_id: SessionId, + _request_id: RequestId, + _data: github_copilot_sdk::PermissionRequestData, + ) -> PermissionResult { + PermissionResult::approve_once().with_context(PermissionDecisionContext { + outcome: PermissionDecisionOutcome::PromptedUser, + source: PermissionDecisionSource::HumanResponse, + surface: PermissionDecisionSurface::CopilotApp, + }) + } +} + #[async_trait] impl McpAuthHandler for CancelMcpAuthHandler { async fn handle( @@ -2495,6 +2514,45 @@ async fn approve_all_handler_approves_permission() { assert_eq!(request["params"]["result"]["kind"], "approve-once"); } +#[tokio::test] +async fn permission_result_forwards_context_beside_result() { + let (_session, mut server) = create_session_pair_with_config(|cfg| { + cfg.with_permission_handler(Arc::new(ContextualApproveHandler)) + }) + .await; + + server + .send_event( + "permission.requested", + serde_json::json!({ + "requestId": "perm-attributed", + "sessionId": server.session_id, + "permissionRequest": { "kind": "shell" }, + }), + ) + .await; + + let request = timeout(TIMEOUT, server.read_request()).await.unwrap(); + assert_eq!( + request["method"], + "session.permissions.handlePendingPermissionRequest" + ); + assert_eq!( + request["params"], + serde_json::json!({ + "sessionId": server.session_id, + "requestId": "perm-attributed", + "result": { "kind": "approve-once" }, + "decisionContext": { + "outcome": "prompted_user", + "source": "human_response", + "surface": "copilot_app", + }, + }) + ); + assert!(request["params"]["result"].get("decisionContext").is_none()); +} + #[tokio::test] async fn session_event_notification_reaches_handler() { let (session, mut server) = create_session_pair().await; diff --git a/test/snapshots/permissions/should_honor_a_decision_annotated_with_decisioncontext.yaml b/test/snapshots/permissions/should_honor_a_decision_annotated_with_decisioncontext.yaml new file mode 100644 index 000000000..ef6f60dbe --- /dev/null +++ b/test/snapshots/permissions/should_honor_a_decision_annotated_with_decisioncontext.yaml @@ -0,0 +1,24 @@ +models: + - claude-sonnet-4.5 +conversations: + - messages: + - role: system + content: ${system} + - role: user + content: Edit protected.txt and replace 'protected' with 'hacked'. + - role: assistant + content: I'll view the file first, then make the edit. + - role: assistant + tool_calls: + - id: toolcall_0 + type: function + function: + name: report_intent + arguments: '{"intent":"Editing protected.txt file"}' + - role: assistant + tool_calls: + - id: toolcall_1 + type: function + function: + name: view + arguments: '{"path":"${workdir}/protected.txt"}' From 93f3542173e92b039699aa2c1eb8324fa71256ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 14 Aug 2026 16:12:16 +0000 Subject: [PATCH 65/72] docs: update version references to 1.0.11 --- java/README.md | 8 ++++---- java/sdk/jbang-example.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/README.md b/java/README.md index e64e22d81..57700a1e9 100644 --- a/java/README.md +++ b/java/README.md @@ -32,14 +32,14 @@ Replace `${copilot.sdk.version}` with the latest release from Maven Central. com.github copilot-sdk-java - 1.0.11-preview.2 + 1.0.11 ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.11-preview.2' +implementation 'com.github:copilot-sdk-java:1.0.11' ``` #### Snapshot Builds @@ -58,7 +58,7 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.12-preview.2-SNAPSHOT + 1.0.12-SNAPSHOT ``` @@ -67,7 +67,7 @@ Snapshot builds of the next development version are published to Maven Central S Replace `${copilot.sdk.version}` with the latest release from Maven Central. ```groovy -implementation 'com.github:copilot-sdk-java:1.0.12-preview.2-SNAPSHOT' +implementation 'com.github:copilot-sdk-java:1.0.12-SNAPSHOT' ``` ## In-process mode (experimental) diff --git a/java/sdk/jbang-example.java b/java/sdk/jbang-example.java index e60509eed..79225cfe7 100644 --- a/java/sdk/jbang-example.java +++ b/java/sdk/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.11-preview.2 +//DEPS com.github:copilot-sdk-java:1.0.11 import com.github.copilot.CopilotClient; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.generated.SessionUsageInfoEvent; From 85ededd134f0d6411de3c044c52236f801a1e5c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 14 Aug 2026 16:13:02 +0000 Subject: [PATCH 66/72] [maven-release-plugin] prepare release java/v1.0.11 --- java/copilot-native/pom.xml | 4 ++-- java/pom.xml | 4 ++-- java/sdk/pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index ef4ec02ab..70b23294a 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.12-preview.2-SNAPSHOT + 1.0.11 ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11 diff --git a/java/pom.xml b/java/pom.xml index cb6662458..82c07b161 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java-parent - 1.0.12-preview.2-SNAPSHOT + 1.0.11 pom GitHub Copilot SDK :: Java :: Parent @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11 diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index 0c93a2b30..eabc32fc8 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.12-preview.2-SNAPSHOT + 1.0.11 ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - HEAD + java/v1.0.11 From e2ee46f8d80cf11d0339cc95fd9ffeefeba7bc5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 14 Aug 2026 16:13:05 +0000 Subject: [PATCH 67/72] [maven-release-plugin] prepare for next development iteration --- java/copilot-native/pom.xml | 4 ++-- java/pom.xml | 4 ++-- java/sdk/pom.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index 70b23294a..7c36801a5 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11 + 1.0.12-SNAPSHOT ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11 + HEAD diff --git a/java/pom.xml b/java/pom.xml index 82c07b161..3f91bf92b 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java-parent - 1.0.11 + 1.0.12-SNAPSHOT pom GitHub Copilot SDK :: Java :: Parent @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11 + HEAD diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index eabc32fc8..05314ba52 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -8,7 +8,7 @@ com.github copilot-sdk-java-parent - 1.0.11 + 1.0.12-SNAPSHOT ../pom.xml @@ -24,7 +24,7 @@ scm:git:https://github.com/github/copilot-sdk.git scm:git:https://github.com/github/copilot-sdk.git https://github.com/github/copilot-sdk - java/v1.0.11 + HEAD From 9d67eb76e93367164a1e11bf0b2fd0833047db45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:08:32 +0000 Subject: [PATCH 68/72] Update @github/copilot to 1.0.80 (#2334) - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- java/pom.xml | 2 +- java/scripts/codegen/package-lock.json | 72 +++++++++++++------------- java/scripts/codegen/package.json | 2 +- nodejs/package-lock.json | 72 +++++++++++++------------- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- test/harness/package-lock.json | 72 +++++++++++++------------- test/harness/package.json | 2 +- 8 files changed, 113 insertions(+), 113 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 3f91bf92b..40e998b8c 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -63,7 +63,7 @@ DO NOT EDIT MANUALLY. Updated by the update-copilot-dependency workflow. --> - ^1.0.79 + ^1.0.80 true diff --git a/java/scripts/codegen/package-lock.json b/java/scripts/codegen/package-lock.json index 527eef03e..69b854f4f 100644 --- a/java/scripts/codegen/package-lock.json +++ b/java/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "json-schema": "^0.4.0", "tsx": "^4.23.1" } @@ -428,9 +428,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", - "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.80.tgz", + "integrity": "sha512-6tf93ZF56KOiTTAjK/UhLZkl1W543IzaTQly288kockJZFswpRTnQEI00Yvacpb39DTvTYu3/ha9SeKpo/pgZQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -439,20 +439,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79", - "@github/copilot-darwin-x64": "1.0.79", - "@github/copilot-linux-arm64": "1.0.79", - "@github/copilot-linux-x64": "1.0.79", - "@github/copilot-linuxmusl-arm64": "1.0.79", - "@github/copilot-linuxmusl-x64": "1.0.79", - "@github/copilot-win32-arm64": "1.0.79", - "@github/copilot-win32-x64": "1.0.79" + "@github/copilot-darwin-arm64": "1.0.80", + "@github/copilot-darwin-x64": "1.0.80", + "@github/copilot-linux-arm64": "1.0.80", + "@github/copilot-linux-x64": "1.0.80", + "@github/copilot-linuxmusl-arm64": "1.0.80", + "@github/copilot-linuxmusl-x64": "1.0.80", + "@github/copilot-win32-arm64": "1.0.80", + "@github/copilot-win32-x64": "1.0.80" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", - "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.80.tgz", + "integrity": "sha512-fzn4PnSx3+O/a3ip72KVsjnzORsEygK+0i21bFAnFBYS+0Wi1Pk+o/CmNsJ7aRbf1enSJrcH8UDVkyc9pMGEBg==", "cpu": [ "arm64" ], @@ -466,9 +466,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", - "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.80.tgz", + "integrity": "sha512-PKsyGk5DccNzR3bYXcYTGB9N6sHzhzGqEwq/2t1qBwqPbrC98Zo2dOT2G40/QYpJ4XdrGmTmdmfPJQ9PJknlIQ==", "cpu": [ "x64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", - "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.80.tgz", + "integrity": "sha512-8oXwN2luyHEjIoSk8AkATBjXDhRoQtuiUvC93GpfQKFHI+I1eoOVwIsAq5fKP8jNCF2rOrYFIcTjwmRt38kCcQ==", "cpu": [ "arm64" ], @@ -498,9 +498,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", - "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.80.tgz", + "integrity": "sha512-qv1ytVNwA3IDK7kcQow+fAikD67t42+AQ8X42bK/7oudNiv4frVZMO0yh1DYIebVRcmEhmPvbVPY/ptVUK3cbA==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", - "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.80.tgz", + "integrity": "sha512-Qjyi+OlVnPC4Lkuy7blDMMwMUQI/yELl7gDnqQlaN8TEbhZqZueuf3p0a+kEjXcNsw4XtNYQc0eMJqSIYy/Pjg==", "cpu": [ "arm64" ], @@ -530,9 +530,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", - "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.80.tgz", + "integrity": "sha512-rBg8pugf+5FhiZxi2zkOr+rlcOVF6Xg63j1FvryfwPT4DJ2w5Na7O3lpS4sgu8QmsP5H+dAqjlXYLYsvSoVQ0g==", "cpu": [ "x64" ], @@ -546,9 +546,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", - "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.80.tgz", + "integrity": "sha512-+f7Vkd3vt2DYOxRnS8dStvYu3DY638N/AuLuIjxZp1F9GgwCUZK69wspqIxg2L59PmRRQcH4AGTrRDR60ENIZA==", "cpu": [ "arm64" ], @@ -562,9 +562,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", - "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.80.tgz", + "integrity": "sha512-PO0kPqhRTWQfsqGaj4UN3cj8ttkcJYy4wmXiArtFm+03AIFu8xTvuhQDPn2xEOsUome7m7t2XomKoavcrCcRsw==", "cpu": [ "x64" ], diff --git a/java/scripts/codegen/package.json b/java/scripts/codegen/package.json index b1393f40b..af331f03a 100644 --- a/java/scripts/codegen/package.json +++ b/java/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "json-schema": "^0.4.0", "tsx": "^4.23.1" } diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 0fe7660f8..4b25e3dce 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" @@ -700,9 +700,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", - "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.80.tgz", + "integrity": "sha512-6tf93ZF56KOiTTAjK/UhLZkl1W543IzaTQly288kockJZFswpRTnQEI00Yvacpb39DTvTYu3/ha9SeKpo/pgZQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "detect-libc": "^2.1.2" @@ -711,20 +711,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79", - "@github/copilot-darwin-x64": "1.0.79", - "@github/copilot-linux-arm64": "1.0.79", - "@github/copilot-linux-x64": "1.0.79", - "@github/copilot-linuxmusl-arm64": "1.0.79", - "@github/copilot-linuxmusl-x64": "1.0.79", - "@github/copilot-win32-arm64": "1.0.79", - "@github/copilot-win32-x64": "1.0.79" + "@github/copilot-darwin-arm64": "1.0.80", + "@github/copilot-darwin-x64": "1.0.80", + "@github/copilot-linux-arm64": "1.0.80", + "@github/copilot-linux-x64": "1.0.80", + "@github/copilot-linuxmusl-arm64": "1.0.80", + "@github/copilot-linuxmusl-x64": "1.0.80", + "@github/copilot-win32-arm64": "1.0.80", + "@github/copilot-win32-x64": "1.0.80" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", - "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.80.tgz", + "integrity": "sha512-fzn4PnSx3+O/a3ip72KVsjnzORsEygK+0i21bFAnFBYS+0Wi1Pk+o/CmNsJ7aRbf1enSJrcH8UDVkyc9pMGEBg==", "cpu": [ "arm64" ], @@ -738,9 +738,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", - "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.80.tgz", + "integrity": "sha512-PKsyGk5DccNzR3bYXcYTGB9N6sHzhzGqEwq/2t1qBwqPbrC98Zo2dOT2G40/QYpJ4XdrGmTmdmfPJQ9PJknlIQ==", "cpu": [ "x64" ], @@ -754,9 +754,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", - "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.80.tgz", + "integrity": "sha512-8oXwN2luyHEjIoSk8AkATBjXDhRoQtuiUvC93GpfQKFHI+I1eoOVwIsAq5fKP8jNCF2rOrYFIcTjwmRt38kCcQ==", "cpu": [ "arm64" ], @@ -770,9 +770,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", - "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.80.tgz", + "integrity": "sha512-qv1ytVNwA3IDK7kcQow+fAikD67t42+AQ8X42bK/7oudNiv4frVZMO0yh1DYIebVRcmEhmPvbVPY/ptVUK3cbA==", "cpu": [ "x64" ], @@ -786,9 +786,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", - "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.80.tgz", + "integrity": "sha512-Qjyi+OlVnPC4Lkuy7blDMMwMUQI/yELl7gDnqQlaN8TEbhZqZueuf3p0a+kEjXcNsw4XtNYQc0eMJqSIYy/Pjg==", "cpu": [ "arm64" ], @@ -802,9 +802,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", - "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.80.tgz", + "integrity": "sha512-rBg8pugf+5FhiZxi2zkOr+rlcOVF6Xg63j1FvryfwPT4DJ2w5Na7O3lpS4sgu8QmsP5H+dAqjlXYLYsvSoVQ0g==", "cpu": [ "x64" ], @@ -818,9 +818,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", - "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.80.tgz", + "integrity": "sha512-+f7Vkd3vt2DYOxRnS8dStvYu3DY638N/AuLuIjxZp1F9GgwCUZK69wspqIxg2L59PmRRQcH4AGTrRDR60ENIZA==", "cpu": [ "arm64" ], @@ -834,9 +834,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", - "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.80.tgz", + "integrity": "sha512-PO0kPqhRTWQfsqGaj4UN3cj8ttkcJYy4wmXiArtFm+03AIFu8xTvuhQDPn2xEOsUome7m7t2XomKoavcrCcRsw==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index 437a77a01..9649c1b36 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index 66b4df470..483a75834 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.0.0-dev", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "koffi": "^3.1.0", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 17a839f0d..be44c52bc 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -501,9 +501,9 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.79.tgz", - "integrity": "sha512-uHBm2BYbKJgyfiKp1WokX7QUNHGvzEX0zaGeb3qM3CybP06rsJrX3JgQe95qwwma6vQz0ah9gV68ERW2JqaKRA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.80.tgz", + "integrity": "sha512-6tf93ZF56KOiTTAjK/UhLZkl1W543IzaTQly288kockJZFswpRTnQEI00Yvacpb39DTvTYu3/ha9SeKpo/pgZQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -513,20 +513,20 @@ "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.79", - "@github/copilot-darwin-x64": "1.0.79", - "@github/copilot-linux-arm64": "1.0.79", - "@github/copilot-linux-x64": "1.0.79", - "@github/copilot-linuxmusl-arm64": "1.0.79", - "@github/copilot-linuxmusl-x64": "1.0.79", - "@github/copilot-win32-arm64": "1.0.79", - "@github/copilot-win32-x64": "1.0.79" + "@github/copilot-darwin-arm64": "1.0.80", + "@github/copilot-darwin-x64": "1.0.80", + "@github/copilot-linux-arm64": "1.0.80", + "@github/copilot-linux-x64": "1.0.80", + "@github/copilot-linuxmusl-arm64": "1.0.80", + "@github/copilot-linuxmusl-x64": "1.0.80", + "@github/copilot-win32-arm64": "1.0.80", + "@github/copilot-win32-x64": "1.0.80" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.79.tgz", - "integrity": "sha512-rsw7JoMvlcxXb0yx08oIeEc0x2hUEwKSfhX9ESKfdMVt0Ckrzm4OEvNUyzOpOnLJ9+l3h/aI+u1w5g2ZU2K7UA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.80.tgz", + "integrity": "sha512-fzn4PnSx3+O/a3ip72KVsjnzORsEygK+0i21bFAnFBYS+0Wi1Pk+o/CmNsJ7aRbf1enSJrcH8UDVkyc9pMGEBg==", "cpu": [ "arm64" ], @@ -541,9 +541,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.79.tgz", - "integrity": "sha512-D983e2lXYnq+KhjA8mTZXonY1+LGfJN9BM195J73shUvx49nRJmibDHWLvVtGeYc+43evGUOAQrOqOspAhhWPQ==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.80.tgz", + "integrity": "sha512-PKsyGk5DccNzR3bYXcYTGB9N6sHzhzGqEwq/2t1qBwqPbrC98Zo2dOT2G40/QYpJ4XdrGmTmdmfPJQ9PJknlIQ==", "cpu": [ "x64" ], @@ -558,9 +558,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.79.tgz", - "integrity": "sha512-qqaNkvi92Wg+4OZk/kTWC2nUG72G0vV6eRAo5+PnKaPmjdX1GsI0a+lPxXPEbzX0zYLi/8yrUyANwyyNEsGgXA==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.80.tgz", + "integrity": "sha512-8oXwN2luyHEjIoSk8AkATBjXDhRoQtuiUvC93GpfQKFHI+I1eoOVwIsAq5fKP8jNCF2rOrYFIcTjwmRt38kCcQ==", "cpu": [ "arm64" ], @@ -575,9 +575,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.79.tgz", - "integrity": "sha512-wzotZfvHkItutciLFMXZT2k9Qiii4Ta8tsVDCMQ7CP8hPxV91FyJ1yf3+FFSSfPvWrfYM6BOAiqIuX+LjgRuiw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.80.tgz", + "integrity": "sha512-qv1ytVNwA3IDK7kcQow+fAikD67t42+AQ8X42bK/7oudNiv4frVZMO0yh1DYIebVRcmEhmPvbVPY/ptVUK3cbA==", "cpu": [ "x64" ], @@ -592,9 +592,9 @@ } }, "node_modules/@github/copilot-linuxmusl-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.79.tgz", - "integrity": "sha512-INtRSARl7DdNm2MXnn4GJuK+Y7QD24ANox02uH8htNQwRlNvdvg+YGS1V/mYgLDXFepeUjMjzTNC+i70+kh5uw==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.80.tgz", + "integrity": "sha512-Qjyi+OlVnPC4Lkuy7blDMMwMUQI/yELl7gDnqQlaN8TEbhZqZueuf3p0a+kEjXcNsw4XtNYQc0eMJqSIYy/Pjg==", "cpu": [ "arm64" ], @@ -609,9 +609,9 @@ } }, "node_modules/@github/copilot-linuxmusl-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.79.tgz", - "integrity": "sha512-LxJAIfPP6Ok/9qpXGZuhnAft3W9JVcK9tbO3jWXcGDJT3v+2NtutyjmP/A7/cDXdTruXVQ4MybwAgacN8Gj/sg==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.80.tgz", + "integrity": "sha512-rBg8pugf+5FhiZxi2zkOr+rlcOVF6Xg63j1FvryfwPT4DJ2w5Na7O3lpS4sgu8QmsP5H+dAqjlXYLYsvSoVQ0g==", "cpu": [ "x64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.79.tgz", - "integrity": "sha512-5wg/ayCBTVy4g4FdO/9BJZRVARY0sgjAn9rBkw5BSJMv4u7Mvxg5Sftlift+V5UWxTyCSHAELZ5IHKvox4Yi8w==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.80.tgz", + "integrity": "sha512-+f7Vkd3vt2DYOxRnS8dStvYu3DY638N/AuLuIjxZp1F9GgwCUZK69wspqIxg2L59PmRRQcH4AGTrRDR60ENIZA==", "cpu": [ "arm64" ], @@ -643,9 +643,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.79", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.79.tgz", - "integrity": "sha512-FTpThWwwCDYnLdE0pfdo5zpAQLLVg36kmC2IKyVMuCYv9iPe7rE1mz7ng/UITN9M3TAMBrwHSvCV3pITvw4W8Q==", + "version": "1.0.80", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.80.tgz", + "integrity": "sha512-PO0kPqhRTWQfsqGaj4UN3cj8ttkcJYy4wmXiArtFm+03AIFu8xTvuhQDPn2xEOsUome7m7t2XomKoavcrCcRsw==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 7903a8f4e..6290d3371 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -14,7 +14,7 @@ "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { - "@github/copilot": "^1.0.79", + "@github/copilot": "^1.0.80", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From a67956bb15135c27e3baf55d04b63e5d65dea729 Mon Sep 17 00:00:00 2001 From: Luke Hoban Date: Fri, 14 Aug 2026 19:15:34 +0000 Subject: [PATCH 69/72] fix(rust): prevent orphaned CLI processes (#2292) * fix(rust): reap spawned process trees Bind each spawned CLI transport to an SDK-owned process tree before it can create descendants. Use a kill-on-close Job Object on Windows and a process group on Unix, and carry the RAII owner through startup, stop, force-stop, and drop paths. Cover grandchild teardown and startup-failure cleanup without changing the public client API. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06 * style(rust): group Windows process imports Match the repository's nightly rustfmt configuration on Linux. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06 * fix(rust): prevent orphaned CLI processes Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * test(rust): reproduce orphaned CLI on client drop Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * test(rust): tolerate slow child startup Generated by Copilot Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Stephen Toub Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06 --- rust/src/lib.rs | 106 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9e3041fec..5c0674469 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1641,8 +1641,8 @@ impl Client { /// notifications via [`ClientInner::lifecycle_tx`] to subscribers /// returned by [`Self::subscribe_lifecycle`]. fn spawn_lifecycle_dispatcher(&self) { - let inner = Arc::clone(&self.inner); - let mut notif_rx = inner.notification_tx.subscribe(); + let mut notif_rx = self.inner.notification_tx.subscribe(); + let lifecycle_tx = self.inner.lifecycle_tx.clone(); tokio::spawn(async move { loop { match notif_rx.recv().await { @@ -1666,7 +1666,7 @@ impl Client { }; // `send` only errors when there are no subscribers — that's // the normal case before any consumer calls subscribe_lifecycle. - let _ = inner.lifecycle_tx.send(event); + let _ = lifecycle_tx.send(event); } Err(tokio::sync::broadcast::error::RecvError::Lagged(n)) => { warn!(missed = n, "lifecycle dispatcher lagged"); @@ -1679,6 +1679,7 @@ impl Client { fn build_command(program: &Path, options: &ClientOptions, working_directory: &Path) -> Command { let mut command = Command::new(program); + command.kill_on_drop(true); for arg in &options.prefix_args { command.arg(arg); } @@ -3270,6 +3271,105 @@ mod tests { client.force_stop(); } + #[cfg(any(unix, windows))] + #[tokio::test] + async fn dropping_last_client_kills_spawned_cli() { + let temp = tempfile::tempdir().unwrap(); + let ready = temp.path().join("ready"); + let survived = temp.path().join("survived"); + let child = test_child_command(temp.path(), &ready, &survived) + .spawn() + .unwrap(); + let (client_write, _server_read) = tokio::io::duplex(64); + let (_server_write, client_read) = tokio::io::duplex(64); + let client = Client::from_transport( + client_read, + client_write, + Some(child), + temp.path().to_path_buf(), + None, + false, + false, + None, + None, + None, + ClientMode::default(), + ) + .unwrap(); + + wait_for_test_child(&ready).await; + drop(client); + + assert_test_child_killed(&survived).await; + } + + #[cfg(any(unix, windows))] + #[tokio::test] + async fn spawned_child_is_killed_when_dropped() { + let temp = tempfile::tempdir().unwrap(); + let ready = temp.path().join("ready"); + let survived = temp.path().join("survived"); + let child = test_child_command(temp.path(), &ready, &survived) + .spawn() + .unwrap(); + + wait_for_test_child(&ready).await; + drop(child); + + assert_test_child_killed(&survived).await; + } + + #[cfg(any(unix, windows))] + fn test_child_command(temp: &Path, ready: &Path, survived: &Path) -> Command { + #[cfg(unix)] + let mut command = { + let mut command = + Client::build_command(Path::new("sh"), &ClientOptions::default(), temp); + command.args([ + "-c", + "printf ready > \"$READY\"; sleep 1; printf survived > \"$SURVIVED\"", + ]); + command + }; + #[cfg(windows)] + let mut command = { + let mut command = + Client::build_command(Path::new("powershell.exe"), &ClientOptions::default(), temp); + command.args([ + "-NoLogo", + "-NoProfile", + "-NonInteractive", + "-Command", + "Set-Content -LiteralPath $env:READY ready; Start-Sleep -Seconds 1; Set-Content -LiteralPath $env:SURVIVED survived", + ]); + command + }; + command.env("READY", ready).env("SURVIVED", survived); + command + } + + #[cfg(any(unix, windows))] + async fn wait_for_test_child(ready: &Path) { + let deadline = tokio::time::Instant::now() + Duration::from_secs(30); + while !ready.exists() { + assert!( + tokio::time::Instant::now() < deadline, + "child did not report readiness" + ); + tokio::time::sleep(Duration::from_millis(10)).await; + } + } + + #[cfg(any(unix, windows))] + async fn assert_test_child_killed(survived: &Path) { + tokio::time::sleep(Duration::from_millis(1500)).await; + + assert!( + !survived.exists(), + "child survived after its owner was dropped" + ); + } + fn client_with_list_models_handler(handler: Arc) -> Client { Client { inner: Arc::new(ClientInner { From 18002a81fa52d006589b813441f98b53e118fe8a Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Fri, 14 Aug 2026 16:09:02 -0700 Subject: [PATCH 70/72] Fix Java builds with scoped npm registries on macOS (#2338) * Fix Java builds with scoped npm registries on macOS Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Test: replace external blocking processes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/sdk/pom.xml | 2 + .../github/copilot/CliServerManagerTest.java | 9 +-- .../com/github/copilot/JsonRpcClientTest.java | 13 +--- .../java/com/github/copilot/TestProcess.java | 65 +++++++++++++++++++ 4 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 java/sdk/src/test/java/com/github/copilot/TestProcess.java diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index 05314ba52..27478a978 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -201,6 +201,7 @@ ${copilot.sdk.root}/test/harness ci + --omit-lockfile-registry-resolved=true --loglevel ${npm.loglevel} @@ -225,6 +226,7 @@ ci --ignore-scripts + --omit-lockfile-registry-resolved=true --loglevel ${npm.loglevel} diff --git a/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java index 353858135..68555a35b 100644 --- a/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CliServerManagerTest.java @@ -70,20 +70,13 @@ void connectToServerTcpMode() throws Exception { } } - private static Process startBlockingProcess() throws IOException { - boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); - return (isWindows - ? new ProcessBuilder(System.getenv("COMSPEC"), "/c", "more") - : new ProcessBuilder("/usr/bin/cat")).start(); - } - @Test void connectToServerStdioMode() throws Exception { var options = new CopilotClientOptions(); var manager = new CliServerManager(options); // Create a dummy process for stdio mode - Process process = startBlockingProcess(); + Process process = new TestProcess(); try { JsonRpcClient client = manager.connectToServer(process, null, null); assertNotNull(client); diff --git a/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java index d6c0b5e14..79aaea10d 100644 --- a/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java +++ b/java/sdk/src/test/java/com/github/copilot/JsonRpcClientTest.java @@ -133,16 +133,9 @@ void testIsConnectedWithSocketClosed() throws Exception { pair.serverSocket.close(); } - private static Process startBlockingProcess() throws IOException { - boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); - return (isWindows - ? new ProcessBuilder(System.getenv("COMSPEC"), "/c", "more") - : new ProcessBuilder("/usr/bin/cat")).start(); - } - @Test void testIsConnectedWithProcess() throws Exception { - Process proc = startBlockingProcess(); + Process proc = new TestProcess(); try (var client = JsonRpcClient.fromProcess(proc)) { assertTrue(client.isConnected()); } @@ -150,7 +143,7 @@ void testIsConnectedWithProcess() throws Exception { @Test void testIsConnectedWithProcessDead() throws Exception { - Process proc = startBlockingProcess(); + Process proc = new TestProcess(); var client = JsonRpcClient.fromProcess(proc); proc.destroy(); proc.waitFor(5, TimeUnit.SECONDS); @@ -162,7 +155,7 @@ void testIsConnectedWithProcessDead() throws Exception { @Test void testGetProcessReturnsProcess() throws Exception { - Process proc = startBlockingProcess(); + Process proc = new TestProcess(); try (var client = JsonRpcClient.fromProcess(proc)) { assertSame(proc, client.getProcess()); } diff --git a/java/sdk/src/test/java/com/github/copilot/TestProcess.java b/java/sdk/src/test/java/com/github/copilot/TestProcess.java new file mode 100644 index 000000000..f7187ae67 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/TestProcess.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +final class TestProcess extends Process { + + private final CountDownLatch terminated = new CountDownLatch(1); + + @Override + public OutputStream getOutputStream() { + return OutputStream.nullOutputStream(); + } + + @Override + public InputStream getInputStream() { + return InputStream.nullInputStream(); + } + + @Override + public InputStream getErrorStream() { + return InputStream.nullInputStream(); + } + + @Override + public int waitFor() throws InterruptedException { + terminated.await(); + return 0; + } + + @Override + public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException { + return terminated.await(timeout, unit); + } + + @Override + public int exitValue() { + if (isAlive()) { + throw new IllegalThreadStateException("Process has not exited"); + } + return 0; + } + + @Override + public void destroy() { + terminated.countDown(); + } + + @Override + public Process destroyForcibly() { + destroy(); + return this; + } + + @Override + public boolean isAlive() { + return terminated.getCount() > 0; + } +} From 731317c2c4006e73c3d41b68b47efa5b0ce63fe2 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 15 Aug 2026 16:42:29 -0400 Subject: [PATCH 71/72] Fix Anthropic replay content block normalization (#2343) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- test/harness/anthropicMessagesAdapter.ts | 3 +- test/harness/modelProtocolAdapterShared.ts | 2 + test/harness/modelProtocolAdapters.test.ts | 76 ++++++++++++++++++++++ test/harness/replayingCapiProxy.ts | 8 ++- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/test/harness/anthropicMessagesAdapter.ts b/test/harness/anthropicMessagesAdapter.ts index acc74a2bf..36e98c3b3 100644 --- a/test/harness/anthropicMessagesAdapter.ts +++ b/test/harness/anthropicMessagesAdapter.ts @@ -4,6 +4,7 @@ import type { ChatCompletion } from "openai/resources/chat/completions"; import { + canonicalUserMessageSeparator, CanonicalMessage, CanonicalToolCall, formatSseEvent, @@ -178,7 +179,7 @@ function convertAnthropicUserMessage( content: onlyText ? contentParts .map((part) => (part.type === "text" ? part.text : "")) - .join("\n") + .join(canonicalUserMessageSeparator) : [...contentParts], }); contentParts.length = 0; diff --git a/test/harness/modelProtocolAdapterShared.ts b/test/harness/modelProtocolAdapterShared.ts index 1f879da5d..dcfee3876 100644 --- a/test/harness/modelProtocolAdapterShared.ts +++ b/test/harness/modelProtocolAdapterShared.ts @@ -4,6 +4,8 @@ export type JsonObject = Record; +export const canonicalUserMessageSeparator = "\n\n\n"; + export type CanonicalToolCall = { id: string; type: "function"; diff --git a/test/harness/modelProtocolAdapters.test.ts b/test/harness/modelProtocolAdapters.test.ts index ddb6fe40b..c0ed08415 100644 --- a/test/harness/modelProtocolAdapters.test.ts +++ b/test/harness/modelProtocolAdapters.test.ts @@ -128,6 +128,52 @@ async function postJson( } describe("Anthropic Messages adapter", () => { + test("canonicalizes adjacent plain-text content blocks", () => { + const result = JSON.parse( + anthropicMessagesRequestToChatCompletion( + JSON.stringify({ + model: "test-model", + messages: [ + { + role: "user", + content: [ + { type: "text", text: "First prompt" }, + { type: "text", text: "Recovery prompt" }, + ], + }, + ], + }), + ), + ) as { + messages: Array<{ role: string; content: unknown }>; + }; + + expect(result.messages).toEqual([ + { + role: "user", + content: "First prompt\n\n\nRecovery prompt", + }, + ]); + }); + + test.each([ + ["string content", "Hello"], + ["one text block", [{ type: "text", text: "Hello" }]], + ])("preserves a single plain-text user block from %s", (_, content) => { + const result = JSON.parse( + anthropicMessagesRequestToChatCompletion( + JSON.stringify({ + model: "test-model", + messages: [{ role: "user", content }], + }), + ), + ) as { + messages: Array<{ role: string; content: unknown }>; + }; + + expect(result.messages).toEqual([{ role: "user", content: "Hello" }]); + }); + test("normalizes messages, binary content, and tools", () => { const result = JSON.parse( anthropicMessagesRequestToChatCompletion( @@ -589,6 +635,36 @@ describe("protocol-aware replay", () => { }); }); + test("canonicalizes Anthropic content blocks before coalescing user messages", async () => { + await writeSnapshot([ + { role: "system", content: "${system}" }, + { role: "user", content: "First prompt" }, + { role: "user", content: "Recovery prompt" }, + { role: "user", content: "Final prompt" }, + { role: "assistant", content: "Recovered" }, + ]); + const request = requestFor("anthropic-messages", "Final prompt"); + request.messages = [ + { + role: "user", + content: [ + { type: "text", text: "First prompt" }, + { type: "text", text: "Recovery prompt" }, + ], + }, + ...(request.messages as unknown[]), + ]; + + await withProxy("anthropic-messages", async (proxyUrl) => { + const response = await postJson( + proxyUrl, + endpoints["anthropic-messages"], + request, + ); + expect(response.status).toBe(200); + }); + }); + test.each(backends)( "replays compaction responses through %s", async (backend) => { diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts index 4c1be59f2..19f00a6e6 100644 --- a/test/harness/replayingCapiProxy.ts +++ b/test/harness/replayingCapiProxy.ts @@ -26,6 +26,7 @@ import { chatCompletionResponseToAnthropicMessage, chatCompletionResponseToAnthropicSseChunks, } from "./anthropicMessagesAdapter"; +import { canonicalUserMessageSeparator } from "./modelProtocolAdapterShared"; import { chatCompletionResponseToResponsesApiMessage, chatCompletionResponseToResponsesApiSseChunks, @@ -983,7 +984,7 @@ function coalesceAdjacentUserMessages(requestBody: string): string { typeof previous.content === "string" && typeof message.content === "string" ) { - previous.content = `${previous.content.trimEnd()}\n\n\n${message.content.trimStart()}`; + previous.content = `${previous.content.trimEnd()}${canonicalUserMessageSeparator}${message.content.trimStart()}`; } else { messages.push(message); } @@ -993,7 +994,7 @@ function coalesceAdjacentUserMessages(requestBody: string): string { if (message.role === "user" && typeof message.content === "string") { message.content = normalizeUserMessage(message.content).replace( /\n{5,}/g, - "\n\n\n", + canonicalUserMessageSeparator, ); } } @@ -1348,7 +1349,8 @@ function coalesceMessages( continue; } - const separator = message.role === "user" ? "\n\n\n" : ""; + const separator = + message.role === "user" ? canonicalUserMessageSeparator : ""; const previousContent = previous.content ?? ""; const currentContent = message.content ?? ""; const content = `${previousContent}${previousContent && currentContent ? separator : ""}${currentContent}`; From 4472fcb9ad342b02aae14ccc3cf1c8083603863e Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Sat, 15 Aug 2026 15:37:29 -0700 Subject: [PATCH 72/72] I assert this is necessary to enable the Java tests to run. (#2340) * Fix npm registry resolution for Copilot updates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply project npm lockfile policy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f787ed29-2541-42ff-85da-d3baef89d667 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f787ed29-2541-42ff-85da-d3baef89d667 --- java/sdk/pom.xml | 2 - nodejs/.npmrc | 1 + nodejs/package-lock.json | 284 --------------------------------- test/harness/.npmrc | 1 + test/harness/package-lock.json | 210 ------------------------ 5 files changed, 2 insertions(+), 496 deletions(-) create mode 100644 nodejs/.npmrc create mode 100644 test/harness/.npmrc diff --git a/java/sdk/pom.xml b/java/sdk/pom.xml index 27478a978..05314ba52 100644 --- a/java/sdk/pom.xml +++ b/java/sdk/pom.xml @@ -201,7 +201,6 @@ ${copilot.sdk.root}/test/harness ci - --omit-lockfile-registry-resolved=true --loglevel ${npm.loglevel} @@ -226,7 +225,6 @@ ci --ignore-scripts - --omit-lockfile-registry-resolved=true --loglevel ${npm.loglevel} diff --git a/nodejs/.npmrc b/nodejs/.npmrc new file mode 100644 index 000000000..100721e01 --- /dev/null +++ b/nodejs/.npmrc @@ -0,0 +1 @@ +omit-lockfile-registry-resolved=true diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 4b25e3dce..a6e0c40c2 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -40,7 +40,6 @@ }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "11.9.3", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", "dev": true, "license": "MIT", @@ -58,7 +57,6 @@ }, "node_modules/@emnapi/core": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", @@ -70,7 +68,6 @@ }, "node_modules/@emnapi/runtime": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", @@ -81,7 +78,6 @@ }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", @@ -92,7 +88,6 @@ }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" @@ -109,7 +104,6 @@ }, "node_modules/@esbuild/android-arm": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" @@ -126,7 +120,6 @@ }, "node_modules/@esbuild/android-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" @@ -143,7 +136,6 @@ }, "node_modules/@esbuild/android-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" @@ -160,7 +152,6 @@ }, "node_modules/@esbuild/darwin-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" @@ -177,7 +168,6 @@ }, "node_modules/@esbuild/darwin-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" @@ -194,7 +184,6 @@ }, "node_modules/@esbuild/freebsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" @@ -211,7 +200,6 @@ }, "node_modules/@esbuild/freebsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" @@ -228,7 +216,6 @@ }, "node_modules/@esbuild/linux-arm": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" @@ -245,7 +232,6 @@ }, "node_modules/@esbuild/linux-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" @@ -262,7 +248,6 @@ }, "node_modules/@esbuild/linux-ia32": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" @@ -279,7 +264,6 @@ }, "node_modules/@esbuild/linux-loong64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" @@ -296,7 +280,6 @@ }, "node_modules/@esbuild/linux-mips64el": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" @@ -313,7 +296,6 @@ }, "node_modules/@esbuild/linux-ppc64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" @@ -330,7 +312,6 @@ }, "node_modules/@esbuild/linux-riscv64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" @@ -347,7 +328,6 @@ }, "node_modules/@esbuild/linux-s390x": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" @@ -364,7 +344,6 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" @@ -381,7 +360,6 @@ }, "node_modules/@esbuild/netbsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" @@ -398,7 +376,6 @@ }, "node_modules/@esbuild/netbsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" @@ -415,7 +392,6 @@ }, "node_modules/@esbuild/openbsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" @@ -432,7 +408,6 @@ }, "node_modules/@esbuild/openbsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" @@ -449,7 +424,6 @@ }, "node_modules/@esbuild/openharmony-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" @@ -466,7 +440,6 @@ }, "node_modules/@esbuild/sunos-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" @@ -483,7 +456,6 @@ }, "node_modules/@esbuild/win32-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" @@ -500,7 +472,6 @@ }, "node_modules/@esbuild/win32-ia32": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" @@ -517,7 +488,6 @@ }, "node_modules/@esbuild/win32-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" @@ -534,7 +504,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", @@ -553,7 +522,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", @@ -563,7 +531,6 @@ }, "node_modules/@eslint/config-array": { "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", @@ -578,7 +545,6 @@ }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", @@ -591,7 +557,6 @@ }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", @@ -604,7 +569,6 @@ }, "node_modules/@eslint/core": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", @@ -617,7 +581,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", @@ -641,7 +604,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", @@ -651,7 +613,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", @@ -664,7 +625,6 @@ }, "node_modules/@eslint/js": { "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", @@ -677,7 +637,6 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", @@ -687,7 +646,6 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", @@ -701,7 +659,6 @@ }, "node_modules/@github/copilot": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.80.tgz", "integrity": "sha512-6tf93ZF56KOiTTAjK/UhLZkl1W543IzaTQly288kockJZFswpRTnQEI00Yvacpb39DTvTYu3/ha9SeKpo/pgZQ==", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -723,7 +680,6 @@ }, "node_modules/@github/copilot-darwin-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.80.tgz", "integrity": "sha512-fzn4PnSx3+O/a3ip72KVsjnzORsEygK+0i21bFAnFBYS+0Wi1Pk+o/CmNsJ7aRbf1enSJrcH8UDVkyc9pMGEBg==", "cpu": [ "arm64" @@ -739,7 +695,6 @@ }, "node_modules/@github/copilot-darwin-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.80.tgz", "integrity": "sha512-PKsyGk5DccNzR3bYXcYTGB9N6sHzhzGqEwq/2t1qBwqPbrC98Zo2dOT2G40/QYpJ4XdrGmTmdmfPJQ9PJknlIQ==", "cpu": [ "x64" @@ -755,7 +710,6 @@ }, "node_modules/@github/copilot-linux-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.80.tgz", "integrity": "sha512-8oXwN2luyHEjIoSk8AkATBjXDhRoQtuiUvC93GpfQKFHI+I1eoOVwIsAq5fKP8jNCF2rOrYFIcTjwmRt38kCcQ==", "cpu": [ "arm64" @@ -771,7 +725,6 @@ }, "node_modules/@github/copilot-linux-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.80.tgz", "integrity": "sha512-qv1ytVNwA3IDK7kcQow+fAikD67t42+AQ8X42bK/7oudNiv4frVZMO0yh1DYIebVRcmEhmPvbVPY/ptVUK3cbA==", "cpu": [ "x64" @@ -787,7 +740,6 @@ }, "node_modules/@github/copilot-linuxmusl-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.80.tgz", "integrity": "sha512-Qjyi+OlVnPC4Lkuy7blDMMwMUQI/yELl7gDnqQlaN8TEbhZqZueuf3p0a+kEjXcNsw4XtNYQc0eMJqSIYy/Pjg==", "cpu": [ "arm64" @@ -803,7 +755,6 @@ }, "node_modules/@github/copilot-linuxmusl-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.80.tgz", "integrity": "sha512-rBg8pugf+5FhiZxi2zkOr+rlcOVF6Xg63j1FvryfwPT4DJ2w5Na7O3lpS4sgu8QmsP5H+dAqjlXYLYsvSoVQ0g==", "cpu": [ "x64" @@ -819,7 +770,6 @@ }, "node_modules/@github/copilot-win32-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.80.tgz", "integrity": "sha512-+f7Vkd3vt2DYOxRnS8dStvYu3DY638N/AuLuIjxZp1F9GgwCUZK69wspqIxg2L59PmRRQcH4AGTrRDR60ENIZA==", "cpu": [ "arm64" @@ -835,7 +785,6 @@ }, "node_modules/@github/copilot-win32-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.80.tgz", "integrity": "sha512-PO0kPqhRTWQfsqGaj4UN3cj8ttkcJYy4wmXiArtFm+03AIFu8xTvuhQDPn2xEOsUome7m7t2XomKoavcrCcRsw==", "cpu": [ "x64" @@ -851,14 +800,12 @@ }, "node_modules/@glideapps/ts-necessities": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@glideapps/ts-necessities/-/ts-necessities-2.2.3.tgz", "integrity": "sha512-gXi0awOZLHk3TbW55GZLCPP6O+y/b5X1pBXKBVckFONSwF1z1E5ND2BGJsghQFah+pW7pkkyFb2VhUQI2qhL5w==", "dev": true, "license": "MIT" }, "node_modules/@humanfs/core": { "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", @@ -868,7 +815,6 @@ }, "node_modules/@humanfs/node": { "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", @@ -882,7 +828,6 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", @@ -896,7 +841,6 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", @@ -910,21 +854,18 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jsdevtools/ono": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "dev": true, "license": "MIT" }, "node_modules/@koromix/koffi-darwin-arm64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-darwin-arm64/-/koffi-darwin-arm64-3.1.0.tgz", "integrity": "sha512-VEt5r3fXTfbejr83PnuOP0H7s9Zmazcs+lofu96DOcRkistlMsn59wYyWiKpyAjs9PCgm0Ykh62ChZ3CGMmIOg==", "cpu": [ "arm64" @@ -940,7 +881,6 @@ }, "node_modules/@koromix/koffi-darwin-x64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-darwin-x64/-/koffi-darwin-x64-3.1.0.tgz", "integrity": "sha512-n/tVRB9xIzdXT5H3zZt8ueThgWTSDL+yU7PWnU8wbZPBSawP/otx3swQyd6nMOqj1bmHgSHopiKSBXRS9pllmg==", "cpu": [ "x64" @@ -956,7 +896,6 @@ }, "node_modules/@koromix/koffi-freebsd-arm64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-arm64/-/koffi-freebsd-arm64-3.1.0.tgz", "integrity": "sha512-vazoPYIhOAlXZksVIqDRMIID4VeUZKx8F3dR90hOobT2ATyOkqNS5dv5UCV7Q7DSq22lQTrdbvENBAhROzCp0w==", "cpu": [ "arm64" @@ -972,7 +911,6 @@ }, "node_modules/@koromix/koffi-freebsd-ia32": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-ia32/-/koffi-freebsd-ia32-3.1.0.tgz", "integrity": "sha512-Vm7Uc97ru6RTSVmae2zCZZQeaizqVZ8WoU4+gG4H03Qe+WOj7kbKt/MxT7VBzdbPYIU5ZJeG/ZED1YlZyab6eQ==", "cpu": [ "ia32" @@ -988,7 +926,6 @@ }, "node_modules/@koromix/koffi-freebsd-x64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-freebsd-x64/-/koffi-freebsd-x64-3.1.0.tgz", "integrity": "sha512-N+VuVWjoiYPy1Go5mRadZ3B6RM5Qz+eCLhj2LXrMlefbUJ+O4gg7teCUGvPGfBEHDgmSN4yYUrfQmdJC10vOYw==", "cpu": [ "x64" @@ -1004,7 +941,6 @@ }, "node_modules/@koromix/koffi-linux-arm64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-arm64/-/koffi-linux-arm64-3.1.0.tgz", "integrity": "sha512-Wx5iOkeALe2ympLdiYwRpIg5qUkyQIv8N2foZ9rRker0uE7ZtXew2RRkbEgMir4b0yDYR1zyXd6B62GUzLtZ/g==", "cpu": [ "arm64" @@ -1020,7 +956,6 @@ }, "node_modules/@koromix/koffi-linux-ia32": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-ia32/-/koffi-linux-ia32-3.1.0.tgz", "integrity": "sha512-1DjYm1QehXU0dgn0uE+FGYOb3Of7GiTMqLS+ZI2gbl1b+h76sz4LRBvDVrQyAmSMVVU8/7696S21YgE/iBhBVg==", "cpu": [ "ia32" @@ -1036,7 +971,6 @@ }, "node_modules/@koromix/koffi-linux-loong64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-loong64/-/koffi-linux-loong64-3.1.0.tgz", "integrity": "sha512-NOa0LdyltdESz3oeTqUH6MErHVoJOHoeXIsEp6xIMTUh4eKXEtlDQeoK6EYqo0DnBt83Xud95qLvi4Aw12pG4Q==", "cpu": [ "loong64" @@ -1052,7 +986,6 @@ }, "node_modules/@koromix/koffi-linux-riscv64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-riscv64/-/koffi-linux-riscv64-3.1.0.tgz", "integrity": "sha512-Ye6kiXZCGxGtAIXSly6XuOP5tJZNYOZ2eVg33k1MilKrzimAy9Mpw4d6e9+Sfsc1jesgeNYs1sb5iaI8HS3ncA==", "cpu": [ "riscv64" @@ -1068,7 +1001,6 @@ }, "node_modules/@koromix/koffi-linux-x64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-linux-x64/-/koffi-linux-x64-3.1.0.tgz", "integrity": "sha512-3yQTOkQrMna4VX+yeyfYImBjLlGrItMpsWyfaW1uSiz/A6GRydqdwYH7DWnp4Z+RSGYZpsewkf7byMc8pOOQKA==", "cpu": [ "x64" @@ -1084,7 +1016,6 @@ }, "node_modules/@koromix/koffi-openbsd-ia32": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-openbsd-ia32/-/koffi-openbsd-ia32-3.1.0.tgz", "integrity": "sha512-/cDoFHb9yx4+yoT3GUpnKnfi3W2drG+/Ewo0TTZaQHb4PsxnYYyT6V8+t4cL5XXbQcTTcOsZxpmBRrn0NBa3dA==", "cpu": [ "ia32" @@ -1100,7 +1031,6 @@ }, "node_modules/@koromix/koffi-openbsd-x64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-openbsd-x64/-/koffi-openbsd-x64-3.1.0.tgz", "integrity": "sha512-CoQdqgnKvWgTXXZlUst8cBRQEov7QsxlTN2WAsu9wez01Xe6gEcH/zYePANualzzCbnaELfe5P0rA80QkoDuPA==", "cpu": [ "x64" @@ -1116,7 +1046,6 @@ }, "node_modules/@koromix/koffi-win32-ia32": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-win32-ia32/-/koffi-win32-ia32-3.1.0.tgz", "integrity": "sha512-WjrA+DEkpy0xEHu48+NSOboHhTnzkIfsFuq3d/WrSs+T9WflWRng3jC7mdJxmR4eHb6i6BqjW3k/U0mNUTjFPA==", "cpu": [ "ia32" @@ -1132,7 +1061,6 @@ }, "node_modules/@koromix/koffi-win32-x64": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@koromix/koffi-win32-x64/-/koffi-win32-x64-3.1.0.tgz", "integrity": "sha512-tnK5+IkzQBauQAQSzuyjso8OOIQRlaTZS39xIWpfqVYDLVDIuLDQk/WwHcOrR5yxlDrZq9ygiebBTOfcJFia7w==", "cpu": [ "x64" @@ -1148,7 +1076,6 @@ }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", @@ -1167,7 +1094,6 @@ }, "node_modules/@oxc-project/types": { "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", @@ -1177,7 +1103,6 @@ }, "node_modules/@platformatic/vfs": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@platformatic/vfs/-/vfs-0.3.0.tgz", "integrity": "sha512-BGXVOAz59HYPZCgI9v/MtiTF/ng8YAWtkooxVwOPR3TatNgGy0WZ/t15ScqytiZi5NdSRqWNRfuAbXKeAlKDdQ==", "dev": true, "license": "MIT", @@ -1187,7 +1112,6 @@ }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" @@ -1204,7 +1128,6 @@ }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" @@ -1221,7 +1144,6 @@ }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" @@ -1238,7 +1160,6 @@ }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" @@ -1255,7 +1176,6 @@ }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" @@ -1272,7 +1192,6 @@ }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" @@ -1289,7 +1208,6 @@ }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" @@ -1306,7 +1224,6 @@ }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" @@ -1323,7 +1240,6 @@ }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" @@ -1340,7 +1256,6 @@ }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" @@ -1357,7 +1272,6 @@ }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" @@ -1374,7 +1288,6 @@ }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" @@ -1391,7 +1304,6 @@ }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" @@ -1410,7 +1322,6 @@ }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" @@ -1427,7 +1338,6 @@ }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" @@ -1444,21 +1354,18 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", @@ -1469,7 +1376,6 @@ }, "node_modules/@types/chai": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", @@ -1480,35 +1386,30 @@ }, "node_modules/@types/deep-eql": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "25.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "dev": true, "license": "MIT", @@ -1518,7 +1419,6 @@ }, "node_modules/@types/ws": { "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "dev": true, "license": "MIT", @@ -1528,7 +1428,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", "dev": true, "license": "MIT", @@ -1557,7 +1456,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", @@ -1582,7 +1480,6 @@ }, "node_modules/@typescript-eslint/project-service": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "license": "MIT", @@ -1604,7 +1501,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "license": "MIT", @@ -1622,7 +1518,6 @@ }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true, "license": "MIT", @@ -1639,7 +1534,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", "dev": true, "license": "MIT", @@ -1664,7 +1558,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true, "license": "MIT", @@ -1678,7 +1571,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "license": "MIT", @@ -1706,7 +1598,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", "dev": true, "license": "MIT", @@ -1730,7 +1621,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "license": "MIT", @@ -1748,7 +1638,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", @@ -1761,7 +1650,6 @@ }, "node_modules/@vitest/expect": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", @@ -1779,7 +1667,6 @@ }, "node_modules/@vitest/mocker": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", @@ -1806,7 +1693,6 @@ }, "node_modules/@vitest/pretty-format": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", @@ -1819,7 +1705,6 @@ }, "node_modules/@vitest/runner": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", @@ -1833,7 +1718,6 @@ }, "node_modules/@vitest/snapshot": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", @@ -1849,7 +1733,6 @@ }, "node_modules/@vitest/spy": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", @@ -1859,7 +1742,6 @@ }, "node_modules/@vitest/utils": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", @@ -1874,7 +1756,6 @@ }, "node_modules/abort-controller": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dev": true, "license": "MIT", @@ -1887,7 +1768,6 @@ }, "node_modules/acorn": { "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", @@ -1900,7 +1780,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", @@ -1910,7 +1789,6 @@ }, "node_modules/ajv": { "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", @@ -1927,14 +1805,12 @@ }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", @@ -1944,14 +1820,12 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ @@ -1972,7 +1846,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", @@ -1983,14 +1856,12 @@ }, "node_modules/browser-or-node": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-3.0.0.tgz", "integrity": "sha512-iczIdVJzGEYhP5DqQxYM9Hh7Ztpqqi+CXZpSmX8ALFs9ecXkQIeqRyM6TfxEfMVpwhl3dSuDvxdzzo9sUOIVBQ==", "dev": true, "license": "MIT" }, "node_modules/buffer": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, "funding": [ @@ -2015,7 +1886,6 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", @@ -2025,7 +1895,6 @@ }, "node_modules/chai": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", @@ -2035,7 +1904,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", @@ -2052,7 +1920,6 @@ }, "node_modules/chalk/node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", @@ -2068,14 +1935,12 @@ }, "node_modules/collection-utils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collection-utils/-/collection-utils-1.0.1.tgz", "integrity": "sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==", "dev": true, "license": "Apache-2.0" }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", @@ -2088,28 +1953,24 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cross-fetch": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", "dev": true, "license": "MIT", @@ -2119,7 +1980,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", @@ -2134,7 +1994,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", @@ -2152,14 +2011,12 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/detect-libc": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "engines": { @@ -2168,14 +2025,12 @@ }, "node_modules/es-module-lexer": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, "node_modules/esbuild": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, @@ -2217,7 +2072,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", @@ -2230,7 +2084,6 @@ }, "node_modules/eslint": { "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", @@ -2290,7 +2143,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", @@ -2307,7 +2159,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", @@ -2320,7 +2171,6 @@ }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", @@ -2333,7 +2183,6 @@ }, "node_modules/eslint/node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", @@ -2343,7 +2192,6 @@ }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", @@ -2356,7 +2204,6 @@ }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", @@ -2374,7 +2221,6 @@ }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", @@ -2387,7 +2233,6 @@ }, "node_modules/esquery": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", @@ -2400,7 +2245,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", @@ -2413,7 +2257,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", @@ -2423,7 +2266,6 @@ }, "node_modules/estree-walker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", @@ -2433,7 +2275,6 @@ }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", @@ -2443,7 +2284,6 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, "license": "MIT", @@ -2453,7 +2293,6 @@ }, "node_modules/events": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", @@ -2463,7 +2302,6 @@ }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", @@ -2473,28 +2311,24 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", @@ -2512,7 +2346,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", @@ -2525,7 +2358,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", @@ -2542,7 +2374,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", @@ -2556,14 +2387,12 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, @@ -2578,7 +2407,6 @@ }, "node_modules/glob": { "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "dev": true, "license": "BlueOak-1.0.0", @@ -2596,7 +2424,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", @@ -2609,7 +2436,6 @@ }, "node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", @@ -2622,7 +2448,6 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", @@ -2632,7 +2457,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ @@ -2653,7 +2477,6 @@ }, "node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", @@ -2663,7 +2486,6 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", @@ -2680,7 +2502,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", @@ -2690,7 +2511,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", @@ -2700,7 +2520,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", @@ -2713,28 +2532,24 @@ }, "node_modules/is-url": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/js-base64": { "version": "3.7.8", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/js-yaml": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, "funding": [ @@ -2757,21 +2572,18 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "dev": true, "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-schema-to-typescript": { "version": "15.0.4", - "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz", "integrity": "sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==", "dev": true, "license": "MIT", @@ -2795,21 +2607,18 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", @@ -2819,7 +2628,6 @@ }, "node_modules/koffi": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/koffi/-/koffi-3.1.0.tgz", "integrity": "sha512-0mCvdjTJBXioiaKNz0vajAEdWtfM5qyhVXSq+wQrrU3odzNvl/J7Cqna79QpNo9mfoKpQgGsyFFDRtDACCwGrQ==", "hasInstallScript": true, "license": "MIT", @@ -2845,7 +2653,6 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", @@ -2859,7 +2666,6 @@ }, "node_modules/lightningcss": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", @@ -2889,7 +2695,6 @@ }, "node_modules/lightningcss-android-arm64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" @@ -2910,7 +2715,6 @@ }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" @@ -2931,7 +2735,6 @@ }, "node_modules/lightningcss-darwin-x64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" @@ -2952,7 +2755,6 @@ }, "node_modules/lightningcss-freebsd-x64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" @@ -2973,7 +2775,6 @@ }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" @@ -2994,7 +2795,6 @@ }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" @@ -3015,7 +2815,6 @@ }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" @@ -3036,7 +2835,6 @@ }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" @@ -3057,7 +2855,6 @@ }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" @@ -3078,7 +2875,6 @@ }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" @@ -3099,7 +2895,6 @@ }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" @@ -3120,7 +2915,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", @@ -3136,21 +2930,18 @@ }, "node_modules/lodash": { "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", "dev": true, "license": "BlueOak-1.0.0", @@ -3160,7 +2951,6 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", @@ -3170,7 +2960,6 @@ }, "node_modules/minimatch": { "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", @@ -3186,7 +2975,6 @@ }, "node_modules/minimatch/node_modules/balanced-match": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", @@ -3196,7 +2984,6 @@ }, "node_modules/minimatch/node_modules/brace-expansion": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", @@ -3209,7 +2996,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", @@ -3219,7 +3005,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", @@ -3229,14 +3014,12 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ @@ -3255,14 +3038,12 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", @@ -3283,7 +3064,6 @@ }, "node_modules/obug": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ @@ -3294,7 +3074,6 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", @@ -3312,7 +3091,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", @@ -3328,7 +3106,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", @@ -3344,21 +3121,18 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true, "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", @@ -3371,7 +3145,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", @@ -3381,7 +3154,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", @@ -3391,7 +3163,6 @@ }, "node_modules/path-scurry": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dev": true, "license": "BlueOak-1.0.0", @@ -3408,21 +3179,18 @@ }, "node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", @@ -3435,7 +3203,6 @@ }, "node_modules/pluralize": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, "license": "MIT", @@ -3445,7 +3212,6 @@ }, "node_modules/postcss": { "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ @@ -3474,7 +3240,6 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", @@ -3484,7 +3249,6 @@ }, "node_modules/prettier": { "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", @@ -3500,7 +3264,6 @@ }, "node_modules/process": { "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, "license": "MIT", @@ -3510,7 +3273,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", @@ -3520,7 +3282,6 @@ }, "node_modules/quicktype-core": { "version": "23.2.6", - "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.2.6.tgz", "integrity": "sha512-asfeSv7BKBNVb9WiYhFRBvBZHcRutPRBwJMxW0pefluK4kkKu4lv0IvZBwFKvw2XygLcL1Rl90zxWDHYgkwCmA==", "dev": true, "license": "Apache-2.0", @@ -3543,7 +3304,6 @@ }, "node_modules/readable-stream": { "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, "license": "MIT", @@ -3560,7 +3320,6 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", @@ -3570,7 +3329,6 @@ }, "node_modules/rimraf": { "version": "6.1.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dev": true, "license": "BlueOak-1.0.0", @@ -3590,7 +3348,6 @@ }, "node_modules/rolldown": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", @@ -3624,7 +3381,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ @@ -3645,7 +3401,6 @@ }, "node_modules/semver": { "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", @@ -3658,7 +3413,6 @@ }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", @@ -3671,7 +3425,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", @@ -3681,14 +3434,12 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", @@ -3698,21 +3449,18 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", @@ -3722,7 +3470,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", @@ -3735,7 +3482,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", @@ -3748,21 +3494,18 @@ }, "node_modules/tiny-inflate": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "dev": true, "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", @@ -3772,7 +3515,6 @@ }, "node_modules/tinyglobby": { "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", @@ -3789,7 +3531,6 @@ }, "node_modules/tinyrainbow": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", @@ -3799,14 +3540,12 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, "license": "MIT" }, "node_modules/ts-api-utils": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", @@ -3819,7 +3558,6 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD", @@ -3827,7 +3565,6 @@ }, "node_modules/tsx": { "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", "dev": true, "license": "MIT", @@ -3846,7 +3583,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", @@ -3859,7 +3595,6 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", @@ -3873,14 +3608,12 @@ }, "node_modules/undici-types": { "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, "node_modules/unicode-properties": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", "dev": true, "license": "MIT", @@ -3891,7 +3624,6 @@ }, "node_modules/unicode-trie": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", "dev": true, "license": "MIT", @@ -3902,14 +3634,12 @@ }, "node_modules/unicode-trie/node_modules/pako": { "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", "dev": true, "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", @@ -3919,14 +3649,12 @@ }, "node_modules/urijs": { "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", "dev": true, "license": "MIT" }, "node_modules/vite": { "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", @@ -4004,7 +3732,6 @@ }, "node_modules/vitest": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", @@ -4094,7 +3821,6 @@ }, "node_modules/vscode-jsonrpc": { "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", "license": "MIT", "engines": { @@ -4103,14 +3829,12 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "license": "MIT", @@ -4121,7 +3845,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", @@ -4137,7 +3860,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", @@ -4154,7 +3876,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", @@ -4164,14 +3885,12 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, "license": "MIT" }, "node_modules/ws": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", @@ -4193,7 +3912,6 @@ }, "node_modules/yaml": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", @@ -4209,7 +3927,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", @@ -4222,7 +3939,6 @@ }, "node_modules/zod": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "funding": { diff --git a/test/harness/.npmrc b/test/harness/.npmrc new file mode 100644 index 000000000..100721e01 --- /dev/null +++ b/test/harness/.npmrc @@ -0,0 +1 @@ +omit-lockfile-registry-resolved=true diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index be44c52bc..ff14c9682 100644 --- a/test/harness/package-lock.json +++ b/test/harness/package-lock.json @@ -26,7 +26,6 @@ }, "node_modules/@emnapi/core": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", @@ -38,7 +37,6 @@ }, "node_modules/@emnapi/runtime": { "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", @@ -49,7 +47,6 @@ }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", @@ -60,7 +57,6 @@ }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" @@ -77,7 +73,6 @@ }, "node_modules/@esbuild/android-arm": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" @@ -94,7 +89,6 @@ }, "node_modules/@esbuild/android-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" @@ -111,7 +105,6 @@ }, "node_modules/@esbuild/android-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" @@ -128,7 +121,6 @@ }, "node_modules/@esbuild/darwin-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" @@ -145,7 +137,6 @@ }, "node_modules/@esbuild/darwin-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" @@ -162,7 +153,6 @@ }, "node_modules/@esbuild/freebsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" @@ -179,7 +169,6 @@ }, "node_modules/@esbuild/freebsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" @@ -196,7 +185,6 @@ }, "node_modules/@esbuild/linux-arm": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" @@ -213,7 +201,6 @@ }, "node_modules/@esbuild/linux-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" @@ -230,7 +217,6 @@ }, "node_modules/@esbuild/linux-ia32": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" @@ -247,7 +233,6 @@ }, "node_modules/@esbuild/linux-loong64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" @@ -264,7 +249,6 @@ }, "node_modules/@esbuild/linux-mips64el": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" @@ -281,7 +265,6 @@ }, "node_modules/@esbuild/linux-ppc64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" @@ -298,7 +281,6 @@ }, "node_modules/@esbuild/linux-riscv64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" @@ -315,7 +297,6 @@ }, "node_modules/@esbuild/linux-s390x": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" @@ -332,7 +313,6 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" @@ -349,7 +329,6 @@ }, "node_modules/@esbuild/netbsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" @@ -366,7 +345,6 @@ }, "node_modules/@esbuild/netbsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" @@ -383,7 +361,6 @@ }, "node_modules/@esbuild/openbsd-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" @@ -400,7 +377,6 @@ }, "node_modules/@esbuild/openbsd-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" @@ -417,7 +393,6 @@ }, "node_modules/@esbuild/openharmony-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" @@ -434,7 +409,6 @@ }, "node_modules/@esbuild/sunos-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" @@ -451,7 +425,6 @@ }, "node_modules/@esbuild/win32-arm64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" @@ -468,7 +441,6 @@ }, "node_modules/@esbuild/win32-ia32": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" @@ -485,7 +457,6 @@ }, "node_modules/@esbuild/win32-x64": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" @@ -502,7 +473,6 @@ }, "node_modules/@github/copilot": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.80.tgz", "integrity": "sha512-6tf93ZF56KOiTTAjK/UhLZkl1W543IzaTQly288kockJZFswpRTnQEI00Yvacpb39DTvTYu3/ha9SeKpo/pgZQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", @@ -525,7 +495,6 @@ }, "node_modules/@github/copilot-darwin-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.80.tgz", "integrity": "sha512-fzn4PnSx3+O/a3ip72KVsjnzORsEygK+0i21bFAnFBYS+0Wi1Pk+o/CmNsJ7aRbf1enSJrcH8UDVkyc9pMGEBg==", "cpu": [ "arm64" @@ -542,7 +511,6 @@ }, "node_modules/@github/copilot-darwin-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.80.tgz", "integrity": "sha512-PKsyGk5DccNzR3bYXcYTGB9N6sHzhzGqEwq/2t1qBwqPbrC98Zo2dOT2G40/QYpJ4XdrGmTmdmfPJQ9PJknlIQ==", "cpu": [ "x64" @@ -559,7 +527,6 @@ }, "node_modules/@github/copilot-linux-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.80.tgz", "integrity": "sha512-8oXwN2luyHEjIoSk8AkATBjXDhRoQtuiUvC93GpfQKFHI+I1eoOVwIsAq5fKP8jNCF2rOrYFIcTjwmRt38kCcQ==", "cpu": [ "arm64" @@ -576,7 +543,6 @@ }, "node_modules/@github/copilot-linux-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.80.tgz", "integrity": "sha512-qv1ytVNwA3IDK7kcQow+fAikD67t42+AQ8X42bK/7oudNiv4frVZMO0yh1DYIebVRcmEhmPvbVPY/ptVUK3cbA==", "cpu": [ "x64" @@ -593,7 +559,6 @@ }, "node_modules/@github/copilot-linuxmusl-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.80.tgz", "integrity": "sha512-Qjyi+OlVnPC4Lkuy7blDMMwMUQI/yELl7gDnqQlaN8TEbhZqZueuf3p0a+kEjXcNsw4XtNYQc0eMJqSIYy/Pjg==", "cpu": [ "arm64" @@ -610,7 +575,6 @@ }, "node_modules/@github/copilot-linuxmusl-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.80.tgz", "integrity": "sha512-rBg8pugf+5FhiZxi2zkOr+rlcOVF6Xg63j1FvryfwPT4DJ2w5Na7O3lpS4sgu8QmsP5H+dAqjlXYLYsvSoVQ0g==", "cpu": [ "x64" @@ -627,7 +591,6 @@ }, "node_modules/@github/copilot-win32-arm64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.80.tgz", "integrity": "sha512-+f7Vkd3vt2DYOxRnS8dStvYu3DY638N/AuLuIjxZp1F9GgwCUZK69wspqIxg2L59PmRRQcH4AGTrRDR60ENIZA==", "cpu": [ "arm64" @@ -644,7 +607,6 @@ }, "node_modules/@github/copilot-win32-x64": { "version": "1.0.80", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.80.tgz", "integrity": "sha512-PO0kPqhRTWQfsqGaj4UN3cj8ttkcJYy4wmXiArtFm+03AIFu8xTvuhQDPn2xEOsUome7m7t2XomKoavcrCcRsw==", "cpu": [ "x64" @@ -661,7 +623,6 @@ }, "node_modules/@hono/node-server": { "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", "dev": true, "license": "MIT", @@ -674,14 +635,12 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "dev": true, "license": "MIT", @@ -722,7 +681,6 @@ }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", @@ -741,7 +699,6 @@ }, "node_modules/@oxc-project/types": { "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", @@ -751,7 +708,6 @@ }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" @@ -768,7 +724,6 @@ }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" @@ -785,7 +740,6 @@ }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" @@ -802,7 +756,6 @@ }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" @@ -819,7 +772,6 @@ }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" @@ -836,7 +788,6 @@ }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" @@ -853,7 +804,6 @@ }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" @@ -870,7 +820,6 @@ }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" @@ -887,7 +836,6 @@ }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" @@ -904,7 +852,6 @@ }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" @@ -921,7 +868,6 @@ }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" @@ -938,7 +884,6 @@ }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" @@ -955,7 +900,6 @@ }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" @@ -974,7 +918,6 @@ }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" @@ -991,7 +934,6 @@ }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" @@ -1008,21 +950,18 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", @@ -1033,7 +972,6 @@ }, "node_modules/@types/chai": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", @@ -1044,21 +982,18 @@ }, "node_modules/@types/deep-eql": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "25.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz", "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "dev": true, "license": "MIT", @@ -1068,7 +1003,6 @@ }, "node_modules/@types/node-forge": { "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", "dev": true, "license": "MIT", @@ -1078,7 +1012,6 @@ }, "node_modules/@vitest/expect": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", @@ -1096,7 +1029,6 @@ }, "node_modules/@vitest/mocker": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", @@ -1123,7 +1055,6 @@ }, "node_modules/@vitest/pretty-format": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", @@ -1136,7 +1067,6 @@ }, "node_modules/@vitest/runner": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", @@ -1150,7 +1080,6 @@ }, "node_modules/@vitest/snapshot": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", @@ -1166,7 +1095,6 @@ }, "node_modules/@vitest/spy": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", @@ -1176,7 +1104,6 @@ }, "node_modules/@vitest/utils": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", @@ -1191,7 +1118,6 @@ }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, "license": "MIT", @@ -1205,7 +1131,6 @@ }, "node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", @@ -1222,7 +1147,6 @@ }, "node_modules/ajv-formats": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, "license": "MIT", @@ -1240,7 +1164,6 @@ }, "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", @@ -1250,7 +1173,6 @@ }, "node_modules/body-parser": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "dev": true, "license": "MIT", @@ -1275,7 +1197,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", @@ -1285,7 +1206,6 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", @@ -1299,7 +1219,6 @@ }, "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", @@ -1316,7 +1235,6 @@ }, "node_modules/chai": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", @@ -1326,7 +1244,6 @@ }, "node_modules/content-disposition": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "dev": true, "license": "MIT", @@ -1340,7 +1257,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "license": "MIT", @@ -1350,14 +1266,12 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, "license": "MIT", @@ -1367,7 +1281,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "dev": true, "license": "MIT", @@ -1377,7 +1290,6 @@ }, "node_modules/cors": { "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "dev": true, "license": "MIT", @@ -1395,7 +1307,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", @@ -1410,7 +1321,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", @@ -1428,7 +1338,6 @@ }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "license": "MIT", @@ -1438,7 +1347,6 @@ }, "node_modules/detect-libc": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", @@ -1448,7 +1356,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", @@ -1463,14 +1370,12 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true, "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", @@ -1480,7 +1385,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", @@ -1490,7 +1394,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "license": "MIT", @@ -1500,14 +1403,12 @@ }, "node_modules/es-module-lexer": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", @@ -1520,7 +1421,6 @@ }, "node_modules/esbuild": { "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, @@ -1562,14 +1462,12 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true, "license": "MIT" }, "node_modules/estree-walker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", @@ -1579,7 +1477,6 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "license": "MIT", @@ -1589,7 +1486,6 @@ }, "node_modules/eventsource": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", "dev": true, "license": "MIT", @@ -1602,7 +1498,6 @@ }, "node_modules/eventsource-parser": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "dev": true, "license": "MIT", @@ -1612,7 +1507,6 @@ }, "node_modules/expect-type": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", @@ -1622,7 +1516,6 @@ }, "node_modules/express": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", @@ -1666,7 +1559,6 @@ }, "node_modules/express-rate-limit": { "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", "dev": true, "license": "MIT", @@ -1685,14 +1577,12 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ @@ -1709,7 +1599,6 @@ }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", @@ -1727,7 +1616,6 @@ }, "node_modules/finalhandler": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", @@ -1749,7 +1637,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, "license": "MIT", @@ -1759,7 +1646,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, "license": "MIT", @@ -1769,7 +1655,6 @@ }, "node_modules/fsevents": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, @@ -1784,7 +1669,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", @@ -1794,7 +1678,6 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", @@ -1819,7 +1702,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, "license": "MIT", @@ -1833,7 +1715,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", @@ -1846,7 +1727,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", @@ -1859,7 +1739,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", @@ -1872,7 +1751,6 @@ }, "node_modules/hono": { "version": "4.12.32", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", "integrity": "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==", "dev": true, "license": "MIT", @@ -1882,7 +1760,6 @@ }, "node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", @@ -1903,7 +1780,6 @@ }, "node_modules/iconv-lite": { "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "dev": true, "license": "MIT", @@ -1920,14 +1796,12 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "license": "ISC" }, "node_modules/ip-address": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", "dev": true, "license": "MIT", @@ -1937,7 +1811,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", @@ -1947,21 +1820,18 @@ }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true, "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/jose": { "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", "dev": true, "license": "MIT", @@ -1971,21 +1841,18 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, "node_modules/json-schema-typed": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "dev": true, "license": "BSD-2-Clause" }, "node_modules/lightningcss": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", @@ -2015,7 +1882,6 @@ }, "node_modules/lightningcss-android-arm64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" @@ -2036,7 +1902,6 @@ }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" @@ -2057,7 +1922,6 @@ }, "node_modules/lightningcss-darwin-x64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" @@ -2078,7 +1942,6 @@ }, "node_modules/lightningcss-freebsd-x64": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" @@ -2099,7 +1962,6 @@ }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" @@ -2120,7 +1982,6 @@ }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" @@ -2141,7 +2002,6 @@ }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" @@ -2162,7 +2022,6 @@ }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" @@ -2183,7 +2042,6 @@ }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" @@ -2204,7 +2062,6 @@ }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" @@ -2225,7 +2082,6 @@ }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" @@ -2246,7 +2102,6 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", @@ -2256,7 +2111,6 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", @@ -2266,7 +2120,6 @@ }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, "license": "MIT", @@ -2276,7 +2129,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, "license": "MIT", @@ -2289,7 +2141,6 @@ }, "node_modules/mime-db": { "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", @@ -2299,7 +2150,6 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", @@ -2316,14 +2166,12 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ @@ -2342,7 +2190,6 @@ }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", @@ -2352,7 +2199,6 @@ }, "node_modules/node-forge": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", @@ -2362,7 +2208,6 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", @@ -2372,7 +2217,6 @@ }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", @@ -2385,7 +2229,6 @@ }, "node_modules/obug": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ @@ -2396,7 +2239,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "license": "MIT", @@ -2409,7 +2251,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", @@ -2419,7 +2260,6 @@ }, "node_modules/openai": { "version": "6.17.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.17.0.tgz", "integrity": "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA==", "dev": true, "license": "Apache-2.0", @@ -2441,7 +2281,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", @@ -2451,7 +2290,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", @@ -2461,7 +2299,6 @@ }, "node_modules/path-to-regexp": { "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "dev": true, "license": "MIT", @@ -2472,21 +2309,18 @@ }, "node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", @@ -2499,7 +2333,6 @@ }, "node_modules/pkce-challenge": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", "dev": true, "license": "MIT", @@ -2509,7 +2342,6 @@ }, "node_modules/postcss": { "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ @@ -2538,7 +2370,6 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "license": "MIT", @@ -2552,7 +2383,6 @@ }, "node_modules/qs": { "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", @@ -2568,7 +2398,6 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, "license": "MIT", @@ -2578,7 +2407,6 @@ }, "node_modules/raw-body": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, "license": "MIT", @@ -2594,7 +2422,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, "license": "MIT", @@ -2604,7 +2431,6 @@ }, "node_modules/rolldown": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", @@ -2638,7 +2464,6 @@ }, "node_modules/router": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "dev": true, "license": "MIT", @@ -2655,14 +2480,12 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, "node_modules/send": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "dev": true, "license": "MIT", @@ -2689,7 +2512,6 @@ }, "node_modules/serve-static": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, "license": "MIT", @@ -2709,14 +2531,12 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true, "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", @@ -2729,7 +2549,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", @@ -2739,7 +2558,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", @@ -2759,7 +2577,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", @@ -2776,7 +2593,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", @@ -2795,7 +2611,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", @@ -2815,14 +2630,12 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", @@ -2832,14 +2645,12 @@ }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", @@ -2849,21 +2660,18 @@ }, "node_modules/std-env": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", @@ -2873,7 +2681,6 @@ }, "node_modules/tinyglobby": { "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", @@ -2890,7 +2697,6 @@ }, "node_modules/tinyrainbow": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", @@ -2900,7 +2706,6 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "license": "MIT", @@ -2910,7 +2715,6 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD", @@ -2918,7 +2722,6 @@ }, "node_modules/tsx": { "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", "dev": true, "license": "MIT", @@ -2937,7 +2740,6 @@ }, "node_modules/type-is": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, "license": "MIT", @@ -2952,7 +2754,6 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", @@ -2966,14 +2767,12 @@ }, "node_modules/undici-types": { "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "license": "MIT", @@ -2983,7 +2782,6 @@ }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, "license": "MIT", @@ -2993,7 +2791,6 @@ }, "node_modules/vite": { "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", @@ -3071,7 +2868,6 @@ }, "node_modules/vitest": { "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", @@ -3161,7 +2957,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", @@ -3177,7 +2972,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", @@ -3194,14 +2988,12 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" }, "node_modules/yaml": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", @@ -3217,7 +3009,6 @@ }, "node_modules/zod": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", @@ -3227,7 +3018,6 @@ }, "node_modules/zod-to-json-schema": { "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "dev": true, "license": "ISC",