From 38e774f969f8b2898cf8b6a79944463d836d32c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 03:34:31 +0000 Subject: [PATCH 1/2] Update @github/copilot to 1.0.45 - Updated nodejs and test harness dependencies - Re-ran code generators - Formatted generated code --- dotnet/src/Generated/Rpc.cs | 74 +++++++++++- dotnet/src/Generated/SessionEvents.cs | 44 +++++++ go/generated_session_events.go | 24 +++- go/rpc/generated_rpc.go | 59 +++++++++- nodejs/package-lock.json | 64 +++++----- nodejs/package.json | 2 +- nodejs/samples/package-lock.json | 2 +- nodejs/src/generated/rpc.ts | 43 ++++++- nodejs/src/generated/session-events.ts | 36 +++++- python/copilot/generated/rpc.py | 131 ++++++++++++++++++++- python/copilot/generated/session_events.py | 27 +++++ rust/src/generated/api_types.rs | 47 +++++++- rust/src/generated/rpc.rs | 18 +++ rust/src/generated/session_events.rs | 44 +++++++ test/harness/package-lock.json | 56 ++++----- test/harness/package.json | 2 +- 16 files changed, 583 insertions(+), 90 deletions(-) diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 50965b901..c81e061c6 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -846,10 +846,6 @@ public sealed class WorkspacesGetWorkspaceResultWorkspace [JsonPropertyName("repository")] public string? Repository { get; set; } - /// Gets or sets the summary value. - [JsonPropertyName("summary")] - public string? Summary { get; set; } - /// Gets or sets the summary_count value. [Range((double)0, (double)long.MaxValue)] [JsonPropertyName("summary_count")] @@ -1792,6 +1788,66 @@ internal sealed class CommandsHandlePendingCommandRequest public string SessionId { get; set; } = string.Empty; } +/// RPC data type for CommandsRespondToQueuedCommand operations. +public sealed class CommandsRespondToQueuedCommandResult +{ + /// Whether the response was accepted (false if the requestId was not found or already resolved). + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Result of the queued command execution. +/// Polymorphic base type discriminated by handled. +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "handled", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(QueuedCommandResultTrue), "true")] +[JsonDerivedType(typeof(QueuedCommandResultFalse), "false")] +public partial class QueuedCommandResult +{ + /// The type discriminator. + [JsonPropertyName("handled")] + public virtual string Handled { get; set; } = string.Empty; +} + + +/// The true variant of . +public partial class QueuedCommandResultTrue : QueuedCommandResult +{ + /// + [JsonIgnore] + public override string Handled => "true"; + + /// If true, stop processing remaining queued items. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("stopProcessingQueue")] + public bool? StopProcessingQueue { get; set; } +} + +/// The false variant of . +public partial class QueuedCommandResultFalse : QueuedCommandResult +{ + /// + [JsonIgnore] + public override string Handled => "false"; +} + +/// RPC data type for CommandsRespondToQueuedCommand operations. +internal sealed class CommandsRespondToQueuedCommandRequest +{ + /// Request ID from the queued command event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Result of the queued command execution. + [JsonPropertyName("result")] + public QueuedCommandResult Result { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// The elicitation response (accept with form values, decline, or cancel). public sealed class UIElicitationResponse { @@ -5214,6 +5270,13 @@ public async Task HandlePendingCommandAsync( var request = new CommandsHandlePendingCommandRequest { SessionId = _sessionId, RequestId = requestId, Error = error }; return await CopilotClient.InvokeRpcAsync(_rpc, "session.commands.handlePendingCommand", [request], cancellationToken); } + + /// Calls "session.commands.respondToQueuedCommand". + public async Task RespondToQueuedCommandAsync(string requestId, QueuedCommandResult result, CancellationToken cancellationToken = default) + { + var request = new CommandsRespondToQueuedCommandRequest { SessionId = _sessionId, RequestId = requestId, Result = result }; + return await CopilotClient.InvokeRpcAsync(_rpc, "session.commands.respondToQueuedCommand", [request], cancellationToken); + } } /// Provides session-scoped Ui APIs. @@ -5506,6 +5569,8 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, FuncAssistant response containing text content, optional tool requests, and interaction metadata. public partial class AssistantMessageData { + /// Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("anthropicAdvisorBlocks")] + public object[]? AnthropicAdvisorBlocks { get; set; } + + /// Anthropic advisor model ID used for this response, for timeline display on replay. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("anthropicAdvisorModel")] + public string? AnthropicAdvisorModel { get; set; } + /// The assistant's text response content. [JsonPropertyName("content")] public required string Content { get; set; } @@ -1945,6 +1955,11 @@ public partial class AssistantMessageData [JsonPropertyName("messageId")] public required string MessageId { get; set; } + /// Model that produced this assistant message, if known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("model")] + public string? Model { get; set; } + /// Actual output token count from the API response (completion_tokens), used for accurate token accounting. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("outputTokens")] @@ -4645,6 +4660,31 @@ public partial class UserToolSessionApprovalCustomTool : UserToolSessionApproval public required string ToolName { get; set; } } +/// The extension-management variant of . +public partial class UserToolSessionApprovalExtensionManagement : UserToolSessionApproval +{ + /// + [JsonIgnore] + public override string Kind => "extension-management"; + + /// Optional operation identifier. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operation")] + public string? Operation { get; set; } +} + +/// The extension-permission-access variant of . +public partial class UserToolSessionApprovalExtensionPermissionAccess : UserToolSessionApproval +{ + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } +} + /// The approval to add as a session-scoped rule. /// Polymorphic base type discriminated by kind. [JsonPolymorphic( @@ -4656,6 +4696,8 @@ public partial class UserToolSessionApprovalCustomTool : UserToolSessionApproval [JsonDerivedType(typeof(UserToolSessionApprovalMcp), "mcp")] [JsonDerivedType(typeof(UserToolSessionApprovalMemory), "memory")] [JsonDerivedType(typeof(UserToolSessionApprovalCustomTool), "custom-tool")] +[JsonDerivedType(typeof(UserToolSessionApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(UserToolSessionApprovalExtensionPermissionAccess), "extension-permission-access")] public partial class UserToolSessionApproval { /// The type discriminator. @@ -6753,6 +6795,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(UserToolSessionApproval))] [JsonSerializable(typeof(UserToolSessionApprovalCommands))] [JsonSerializable(typeof(UserToolSessionApprovalCustomTool))] +[JsonSerializable(typeof(UserToolSessionApprovalExtensionManagement))] +[JsonSerializable(typeof(UserToolSessionApprovalExtensionPermissionAccess))] [JsonSerializable(typeof(UserToolSessionApprovalMcp))] [JsonSerializable(typeof(UserToolSessionApprovalMemory))] [JsonSerializable(typeof(UserToolSessionApprovalRead))] diff --git a/go/generated_session_events.go b/go/generated_session_events.go index 7dc1f3a32..5cb73195f 100644 --- a/go/generated_session_events.go +++ b/go/generated_session_events.go @@ -699,6 +699,10 @@ func (*AssistantReasoningData) sessionEventData() {} // Assistant response containing text content, optional tool requests, and interaction metadata type AssistantMessageData struct { + // Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping + AnthropicAdvisorBlocks []any `json:"anthropicAdvisorBlocks,omitempty"` + // Anthropic advisor model ID used for this response, for timeline display on replay + AnthropicAdvisorModel *string `json:"anthropicAdvisorModel,omitempty"` // The assistant's text response content Content string `json:"content"` // Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. @@ -707,6 +711,8 @@ type AssistantMessageData struct { InteractionID *string `json:"interactionId,omitempty"` // Unique identifier for this assistant message MessageID string `json:"messageId"` + // Model that produced this assistant message, if known + Model *string `json:"model,omitempty"` // Actual output token count from the API response (completion_tokens), used for accurate token accounting OutputTokens *float64 `json:"outputTokens,omitempty"` // Tool call ID of the parent tool invocation when this event originates from a sub-agent @@ -2468,8 +2474,12 @@ type UserMessageAttachmentSelectionDetailsStart struct { type UserToolSessionApproval struct { // Command identifiers approved by the user CommandIdentifiers []string `json:"commandIdentifiers,omitempty"` + // Extension name + ExtensionName *string `json:"extensionName,omitempty"` // Kind discriminator Kind UserToolSessionApprovalKind `json:"kind"` + // Optional operation identifier + Operation *string `json:"operation,omitempty"` // MCP server name ServerName *string `json:"serverName,omitempty"` // Optional MCP tool name, or null for all tools on the server @@ -2791,12 +2801,14 @@ const ( type UserToolSessionApprovalKind string const ( - UserToolSessionApprovalKindCommands UserToolSessionApprovalKind = "commands" - UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKind = "custom-tool" - UserToolSessionApprovalKindMcp UserToolSessionApprovalKind = "mcp" - UserToolSessionApprovalKindMemory UserToolSessionApprovalKind = "memory" - UserToolSessionApprovalKindRead UserToolSessionApprovalKind = "read" - UserToolSessionApprovalKindWrite UserToolSessionApprovalKind = "write" + UserToolSessionApprovalKindCommands UserToolSessionApprovalKind = "commands" + UserToolSessionApprovalKindCustomTool UserToolSessionApprovalKind = "custom-tool" + UserToolSessionApprovalKindExtensionManagement UserToolSessionApprovalKind = "extension-management" + UserToolSessionApprovalKindExtensionPermissionAccess UserToolSessionApprovalKind = "extension-permission-access" + UserToolSessionApprovalKindMcp UserToolSessionApprovalKind = "mcp" + UserToolSessionApprovalKindMemory UserToolSessionApprovalKind = "memory" + UserToolSessionApprovalKindRead UserToolSessionApprovalKind = "read" + UserToolSessionApprovalKindWrite UserToolSessionApprovalKind = "write" ) // Hosting platform type of the repository (github or ado) diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go index 87f323814..cc099b8ea 100644 --- a/go/rpc/generated_rpc.go +++ b/go/rpc/generated_rpc.go @@ -105,6 +105,19 @@ type CommandsHandlePendingCommandResult struct { Success bool `json:"success"` } +type CommandsRespondToQueuedCommandRequest struct { + // Request ID from the queued command event + RequestID string `json:"requestId"` + // Result of the queued command execution + Result QueuedCommandResult `json:"result"` +} + +type CommandsRespondToQueuedCommandResult struct { + // Whether the response was accepted (false if the requestId was not found or already + // resolved) + Success bool `json:"success"` +} + // Internal: ConnectRequest is an internal SDK API and is not part of the public surface. type ConnectRequest struct { // Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN @@ -1109,6 +1122,26 @@ type PluginList struct { Plugins []Plugin `json:"plugins"` } +type QueuedCommandHandled struct { + // The command was handled + Handled bool `json:"handled"` + // If true, stop processing remaining queued items + StopProcessingQueue *bool `json:"stopProcessingQueue,omitempty"` +} + +type QueuedCommandNotHandled struct { + // The command was not handled + Handled bool `json:"handled"` +} + +// Result of the queued command execution +type QueuedCommandResult struct { + // Handled discriminator + Handled QueuedCommandResultHandled `json:"handled"` + // If true, stop processing remaining queued items + StopProcessingQueue *bool `json:"stopProcessingQueue,omitempty"` +} + // Experimental: RemoteDisableResult is part of an experimental API and may change or be // removed. type RemoteDisableResult struct { @@ -1980,7 +2013,6 @@ type WorkspacesGetWorkspaceResultWorkspace struct { Name *string `json:"name,omitempty"` RemoteSteerable *bool `json:"remote_steerable,omitempty"` Repository *string `json:"repository,omitempty"` - Summary *string `json:"summary,omitempty"` SummaryCount *int64 `json:"summary_count,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` UserNamed *bool `json:"user_named,omitempty"` @@ -2396,6 +2428,14 @@ const ( PermissionDecisionUserNotAvailableKindUserNotAvailable PermissionDecisionUserNotAvailableKind = "user-not-available" ) +// Handled discriminator for QueuedCommandResult. +type QueuedCommandResultHandled string + +const ( + QueuedCommandResultHandledFalse QueuedCommandResultHandled = "false" + QueuedCommandResultHandledTrue QueuedCommandResultHandled = "true" +) + // Error classification type SessionFsErrorCode string @@ -2995,6 +3035,23 @@ func (a *CommandsApi) HandlePendingCommand(ctx context.Context, params *Commands return &result, nil } +func (a *CommandsApi) RespondToQueuedCommand(ctx context.Context, params *CommandsRespondToQueuedCommandRequest) (*CommandsRespondToQueuedCommandResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["requestId"] = params.RequestID + req["result"] = params.Result + } + raw, err := a.client.Request("session.commands.respondToQueuedCommand", req) + if err != nil { + return nil, err + } + var result CommandsRespondToQueuedCommandResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: ExtensionsApi contains experimental APIs that may change or be removed. type ExtensionsApi sessionApi diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index ef3bb3fcf..a2f495463 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.45", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -663,26 +663,26 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.44-3.tgz", - "integrity": "sha512-hTsNxnmtKDK3ymh+c6LrsXWc9TbbubUHSxPuAKc4CX0d1c9iI1R4ybzS5Ihe+GxlozHIyFANd58gAg3QH3uCkA==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.45.tgz", + "integrity": "sha512-2QADgQcw/d0GFqTq2+nHwX152ZRvZxW0CHONG5d1RCs6YJtdr/GdbnMYYeRH2BiBIhnfkcvF50ImCRvsS5Tnwg==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.44-3", - "@github/copilot-darwin-x64": "1.0.44-3", - "@github/copilot-linux-arm64": "1.0.44-3", - "@github/copilot-linux-x64": "1.0.44-3", - "@github/copilot-win32-arm64": "1.0.44-3", - "@github/copilot-win32-x64": "1.0.44-3" + "@github/copilot-darwin-arm64": "1.0.45", + "@github/copilot-darwin-x64": "1.0.45", + "@github/copilot-linux-arm64": "1.0.45", + "@github/copilot-linux-x64": "1.0.45", + "@github/copilot-win32-arm64": "1.0.45", + "@github/copilot-win32-x64": "1.0.45" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.44-3.tgz", - "integrity": "sha512-59IXG1lGCf0Ni4TjNL6bqBul6G2FPFX2vh6pMnoRVtHvRrtFILIBMNRMNQFrYZo3eXYBqYXwVHu4R8zfELpK6A==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.45.tgz", + "integrity": "sha512-gCJy1nOIWL5lpLFJTRk2Kz7bS30emkA4p4gM+PJ5/dOwNRBOyUO0/2f03/m5vYL4DNd/T47cFIN6s82gISAIYQ==", "cpu": [ "arm64" ], @@ -696,9 +696,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.44-3.tgz", - "integrity": "sha512-I+aR9rBNzwn3OOd5oIDIpnUCkCtj3mL183Ml1LLUcJ3utxwxKVInckW/Jg36jSD2PhkbNX8gzq0l3dv0td6QYQ==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.45.tgz", + "integrity": "sha512-nLzC7C0i/WAY+4FukHuONBDNeKUAqBBab3n36aEdpqxVDP5h2Tbzg2yShqav2blR7KDJL7YMcYTVFxmwfQj+yQ==", "cpu": [ "x64" ], @@ -712,9 +712,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.44-3.tgz", - "integrity": "sha512-Agz4tMiM0hy9zIPPxKF0SSjMZSYuLYoGMe5KbvNEwTrAApLSrSW6k8yhlOTVCiRHEBsfh69We3LCOmc8hX8jVg==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.45.tgz", + "integrity": "sha512-MdRNZUNMrI0dpQ+DiDoZQ7AbitQp9eN7ir176Za2Kf7dkUxPwmio32yhRbBS81McU6vBw8cCzEZviwv/jc8buQ==", "cpu": [ "arm64" ], @@ -728,9 +728,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.44-3.tgz", - "integrity": "sha512-Ev5/uZKqSOr6l2tcy9Xqx354tuxo8qE42Cnnd6JynGrvVc1NpzF1Kt5eCzzjxdZiRtPo6AdDXS16oAN8CVxCrg==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.45.tgz", + "integrity": "sha512-xSRUjWA+wrSSjktJSjNtiS/47Cy0PviPejj7RUmtChsPfDJB8wW2iZ6NfpdiAomtxAz5xx4AjbjT1I4b1FqnwA==", "cpu": [ "x64" ], @@ -744,9 +744,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.44-3.tgz", - "integrity": "sha512-bV2JeRRNYTiTfqmCVeXdPpgYe8KY58diJFZdhYSQnQDowjKvRn59K0RBEYDGK8//AjN+NfaGPGikMq3CQm61cA==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.45.tgz", + "integrity": "sha512-lhcTlKs7MWMzIXv21hUSpL4aFW49jqVhNrQKaB8sYk2nzvGRJvNwTcBS1Tn5ndXlPzQ9P/p9B6B5uwwmZ1vHHw==", "cpu": [ "arm64" ], @@ -760,9 +760,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.44-3.tgz", - "integrity": "sha512-qR6q16UDC6bIO8cde62z0wwVweH351RzN1KZgMjBqQYUBJw521K8VK7p64XK0tQWoTG8uyCuqqu5djQq/4Ek+g==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.45.tgz", + "integrity": "sha512-XYZ983NQmooVr/n+pCnHIorBmf1hd3o1rMlSAodwG/VFlQaydGoOs1F1NntxWBoFAND+eM6N4PZfw8M8sRayfA==", "cpu": [ "x64" ], @@ -1260,7 +1260,6 @@ "integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -1300,7 +1299,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -1629,7 +1627,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1957,7 +1954,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2866,7 +2862,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3285,7 +3280,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -3319,7 +3313,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3387,7 +3380,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/nodejs/package.json b/nodejs/package.json index 9ed91794f..a9cd04a70 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.45", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index b4b177fc7..1a019f262 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,7 +18,7 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.45", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index b52fecce9..ce95493cb 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -12,6 +12,13 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js"; * via the `definition` "AuthInfoType". */ export type AuthInfoType = "hmac" | "env" | "user" | "gh-cli" | "api-key" | "token" | "copilot-api-token"; +/** + * Result of the queued command execution + * + * This interface was referenced by `_RpcSchemaRoot`'s JSON-Schema + * via the `definition` "QueuedCommandResult". + */ +export type QueuedCommandResult = QueuedCommandHandled | QueuedCommandNotHandled; /** * Server transport type: stdio, http, sse, or memory (local configs are normalized to stdio) * @@ -399,6 +406,39 @@ export interface CommandsHandlePendingCommandResult { success: boolean; } +export interface CommandsRespondToQueuedCommandRequest { + /** + * Request ID from the queued command event + */ + requestId: string; + result: QueuedCommandResult; +} + +export interface QueuedCommandHandled { + /** + * The command was handled + */ + handled: true; + /** + * If true, stop processing remaining queued items + */ + stopProcessingQueue?: boolean; +} + +export interface QueuedCommandNotHandled { + /** + * The command was not handled + */ + handled: false; +} + +export interface CommandsRespondToQueuedCommandResult { + /** + * Whether the response was accepted (false if the requestId was not found or already resolved) + */ + success: boolean; +} + /** @internal */ export interface ConnectRequest { /** @@ -2520,7 +2560,6 @@ export interface WorkspacesGetWorkspaceResult { branch?: string; name?: string; user_named?: boolean; - summary?: string; summary_count?: number; created_at?: string; updated_at?: string; @@ -2752,6 +2791,8 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin commands: { handlePendingCommand: async (params: CommandsHandlePendingCommandRequest): Promise => connection.sendRequest("session.commands.handlePendingCommand", { sessionId, ...params }), + respondToQueuedCommand: async (params: CommandsRespondToQueuedCommandRequest): Promise => + connection.sendRequest("session.commands.respondToQueuedCommand", { sessionId, ...params }), }, ui: { elicitation: async (params: UIElicitationRequest): Promise => diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index a9155ea48..6f2bab31c 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -240,7 +240,9 @@ export type UserToolSessionApproval = | UserToolSessionApprovalWrite | UserToolSessionApprovalMcp | UserToolSessionApprovalMemory - | UserToolSessionApprovalCustomTool; + | UserToolSessionApprovalCustomTool + | UserToolSessionApprovalExtensionManagement + | UserToolSessionApprovalExtensionPermissionAccess; /** * Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. */ @@ -1972,6 +1974,14 @@ export interface AssistantMessageEvent { * Assistant response containing text content, optional tool requests, and interaction metadata */ export interface AssistantMessageData { + /** + * Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping + */ + anthropicAdvisorBlocks?: unknown[]; + /** + * Anthropic advisor model ID used for this response, for timeline display on replay + */ + anthropicAdvisorModel?: string; /** * The assistant's text response content */ @@ -1988,6 +1998,10 @@ export interface AssistantMessageData { * Unique identifier for this assistant message */ messageId: string; + /** + * Model that produced this assistant message, if known + */ + model?: string; /** * Actual output token count from the API response (completion_tokens), used for accurate token accounting */ @@ -4142,6 +4156,26 @@ export interface UserToolSessionApprovalCustomTool { */ toolName: string; } +export interface UserToolSessionApprovalExtensionManagement { + /** + * Extension management approval kind + */ + kind: "extension-management"; + /** + * Optional operation identifier + */ + operation?: string; +} +export interface UserToolSessionApprovalExtensionPermissionAccess { + /** + * Extension name + */ + extensionName: string; + /** + * Extension permission access approval kind + */ + kind: "extension-permission-access"; +} export interface PermissionApprovedForLocation { approval: UserToolSessionApproval; /** diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index ca8fbe494..91d3f7474 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -245,6 +245,50 @@ def to_dict(self) -> dict: result["success"] = from_bool(self.success) return result +@dataclass +class QueuedCommandResult: + """Result of the queued command execution""" + + handled: bool + """The command was handled + + The command was not handled + """ + stop_processing_queue: bool | None = None + """If true, stop processing remaining queued items""" + + @staticmethod + def from_dict(obj: Any) -> 'QueuedCommandResult': + assert isinstance(obj, dict) + handled = from_bool(obj.get("handled")) + stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) + return QueuedCommandResult(handled, stop_processing_queue) + + def to_dict(self) -> dict: + result: dict = {} + result["handled"] = from_bool(self.handled) + if self.stop_processing_queue is not None: + result["stopProcessingQueue"] = from_union([from_bool, from_none], self.stop_processing_queue) + return result + +@dataclass +class CommandsRespondToQueuedCommandResult: + success: bool + """Whether the response was accepted (false if the requestId was not found or already + resolved) + """ + + @staticmethod + def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandResult': + assert isinstance(obj, dict) + success = from_bool(obj.get("success")) + return CommandsRespondToQueuedCommandResult(success) + + def to_dict(self) -> dict: + result: dict = {} + result["success"] = from_bool(self.success) + return result + # Internal: this type is an internal SDK API and is not part of the public surface. @dataclass class ConnectRequest: @@ -1351,6 +1395,44 @@ def to_dict(self) -> dict: result["version"] = from_union([from_str, from_none], self.version) return result +@dataclass +class QueuedCommandHandled: + handled: bool + """The command was handled""" + + stop_processing_queue: bool | None = None + """If true, stop processing remaining queued items""" + + @staticmethod + def from_dict(obj: Any) -> 'QueuedCommandHandled': + assert isinstance(obj, dict) + handled = from_bool(obj.get("handled")) + stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) + return QueuedCommandHandled(handled, stop_processing_queue) + + def to_dict(self) -> dict: + result: dict = {} + result["handled"] = from_bool(self.handled) + if self.stop_processing_queue is not None: + result["stopProcessingQueue"] = from_union([from_bool, from_none], self.stop_processing_queue) + return result + +@dataclass +class QueuedCommandNotHandled: + handled: bool + """The command was not handled""" + + @staticmethod + def from_dict(obj: Any) -> 'QueuedCommandNotHandled': + assert isinstance(obj, dict) + handled = from_bool(obj.get("handled")) + return QueuedCommandNotHandled(handled) + + def to_dict(self) -> dict: + result: dict = {} + result["handled"] = from_bool(self.handled) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class RemoteEnableResult: @@ -2677,6 +2759,27 @@ def to_dict(self) -> dict: result["statusMessage"] = from_union([from_str, from_none], self.status_message) return result +@dataclass +class CommandsRespondToQueuedCommandRequest: + request_id: str + """Request ID from the queued command event""" + + result: QueuedCommandResult + """Result of the queued command execution""" + + @staticmethod + def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = QueuedCommandResult.from_dict(obj.get("result")) + return CommandsRespondToQueuedCommandRequest(request_id, result) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = to_class(QueuedCommandResult, self.result) + return result + @dataclass class DiscoveredMCPServer: enabled: bool @@ -4372,7 +4475,6 @@ class Workspace: name: str | None = None remote_steerable: bool | None = None repository: str | None = None - summary: str | None = None summary_count: int | None = None updated_at: datetime | None = None user_named: bool | None = None @@ -4393,11 +4495,10 @@ def from_dict(obj: Any) -> 'Workspace': name = from_union([from_str, from_none], obj.get("name")) remote_steerable = from_union([from_bool, from_none], obj.get("remote_steerable")) repository = from_union([from_str, from_none], obj.get("repository")) - summary = from_union([from_str, from_none], obj.get("summary")) summary_count = from_union([from_int, from_none], obj.get("summary_count")) updated_at = from_union([from_datetime, from_none], obj.get("updated_at")) user_named = from_union([from_bool, from_none], obj.get("user_named")) - return Workspace(id, branch, chronicle_sync_dismissed, created_at, cwd, git_root, host_type, mc_last_event_id, mc_session_id, mc_task_id, name, remote_steerable, repository, summary, summary_count, updated_at, user_named) + return Workspace(id, branch, chronicle_sync_dismissed, created_at, cwd, git_root, host_type, mc_last_event_id, mc_session_id, mc_task_id, name, remote_steerable, repository, summary_count, updated_at, user_named) def to_dict(self) -> dict: result: dict = {} @@ -4426,8 +4527,6 @@ def to_dict(self) -> dict: result["remote_steerable"] = from_union([from_bool, from_none], self.remote_steerable) if self.repository is not None: result["repository"] = from_union([from_str, from_none], self.repository) - if self.summary is not None: - result["summary"] = from_union([from_str, from_none], self.summary) if self.summary_count is not None: result["summary_count"] = from_union([from_int, from_none], self.summary_count) if self.updated_at is not None: @@ -5779,6 +5878,8 @@ class RPC: auth_info_type: AuthInfoType commands_handle_pending_command_request: CommandsHandlePendingCommandRequest commands_handle_pending_command_result: CommandsHandlePendingCommandResult + commands_respond_to_queued_command_request: CommandsRespondToQueuedCommandRequest + commands_respond_to_queued_command_result: CommandsRespondToQueuedCommandResult connect_request: ConnectRequest connect_result: ConnectResult current_model: CurrentModel @@ -5901,6 +6002,9 @@ class RPC: plan_update_request: PlanUpdateRequest plugin: Plugin plugin_list: PluginList + queued_command_handled: QueuedCommandHandled + queued_command_not_handled: QueuedCommandNotHandled + queued_command_result: QueuedCommandResult remote_enable_result: RemoteEnableResult server_skill: ServerSkill server_skill_list: ServerSkillList @@ -6014,6 +6118,8 @@ def from_dict(obj: Any) -> 'RPC': auth_info_type = AuthInfoType(obj.get("AuthInfoType")) commands_handle_pending_command_request = CommandsHandlePendingCommandRequest.from_dict(obj.get("CommandsHandlePendingCommandRequest")) commands_handle_pending_command_result = CommandsHandlePendingCommandResult.from_dict(obj.get("CommandsHandlePendingCommandResult")) + commands_respond_to_queued_command_request = CommandsRespondToQueuedCommandRequest.from_dict(obj.get("CommandsRespondToQueuedCommandRequest")) + commands_respond_to_queued_command_result = CommandsRespondToQueuedCommandResult.from_dict(obj.get("CommandsRespondToQueuedCommandResult")) connect_request = ConnectRequest.from_dict(obj.get("ConnectRequest")) connect_result = ConnectResult.from_dict(obj.get("ConnectResult")) current_model = CurrentModel.from_dict(obj.get("CurrentModel")) @@ -6136,6 +6242,9 @@ def from_dict(obj: Any) -> 'RPC': plan_update_request = PlanUpdateRequest.from_dict(obj.get("PlanUpdateRequest")) plugin = Plugin.from_dict(obj.get("Plugin")) plugin_list = PluginList.from_dict(obj.get("PluginList")) + queued_command_handled = QueuedCommandHandled.from_dict(obj.get("QueuedCommandHandled")) + queued_command_not_handled = QueuedCommandNotHandled.from_dict(obj.get("QueuedCommandNotHandled")) + queued_command_result = QueuedCommandResult.from_dict(obj.get("QueuedCommandResult")) remote_enable_result = RemoteEnableResult.from_dict(obj.get("RemoteEnableResult")) server_skill = ServerSkill.from_dict(obj.get("ServerSkill")) server_skill_list = ServerSkillList.from_dict(obj.get("ServerSkillList")) @@ -6233,7 +6342,7 @@ def from_dict(obj: Any) -> 'RPC': workspaces_list_files_result = WorkspacesListFilesResult.from_dict(obj.get("WorkspacesListFilesResult")) workspaces_read_file_request = WorkspacesReadFileRequest.from_dict(obj.get("WorkspacesReadFileRequest")) workspaces_read_file_result = WorkspacesReadFileResult.from_dict(obj.get("WorkspacesReadFileResult")) - return RPC(account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_list, agent_reload_result, agent_select_request, agent_select_result, auth_info_type, commands_handle_pending_command_request, commands_handle_pending_command_result, connect_request, connect_result, current_model, discovered_mcp_server, discovered_mcp_server_source, discovered_mcp_server_type, embedded_blob_resource_contents, embedded_text_resource_contents, extension, 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_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_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, filter_mapping_string, filter_mapping_value, fleet_start_request, fleet_start_result, handle_pending_tool_call_request, handle_pending_tool_call_result, history_compact_context_window, history_compact_result, history_truncate_request, history_truncate_result, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_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_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_oauth_login_request, mcp_oauth_login_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_local, mcp_server_config_local_type, mcp_server_list, mcp_server_source, mcp_server_status, model, model_billing, 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_policy, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_request, permission_decision, 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_reject, permission_decision_request, permission_decision_user_not_available, permission_request_result, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, remote_enable_result, server_skill, server_skill_list, session_auth_status, 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_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_log_level, session_mode, sessions_fork_request, sessions_fork_result, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, task_agent_info, task_agent_info_execution_mode, task_agent_info_status, task_info, task_list, tasks_cancel_request, tasks_cancel_result, task_shell_info, task_shell_info_attachment_mode, task_shell_info_execution_mode, task_shell_info_status, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, tool, tool_list, tools_list_request, 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_handle_pending_elicitation_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, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_files_result, workspaces_read_file_request, workspaces_read_file_result) + return RPC(account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_list, agent_reload_result, agent_select_request, agent_select_result, auth_info_type, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, connect_request, connect_result, current_model, discovered_mcp_server, discovered_mcp_server_source, discovered_mcp_server_type, embedded_blob_resource_contents, embedded_text_resource_contents, extension, 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_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_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, filter_mapping_string, filter_mapping_value, fleet_start_request, fleet_start_result, handle_pending_tool_call_request, handle_pending_tool_call_result, history_compact_context_window, history_compact_result, history_truncate_request, history_truncate_result, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_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_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_oauth_login_request, mcp_oauth_login_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_local, mcp_server_config_local_type, mcp_server_list, mcp_server_source, mcp_server_status, model, model_billing, 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_policy, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_request, permission_decision, 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_reject, permission_decision_request, permission_decision_user_not_available, permission_request_result, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, queued_command_handled, queued_command_not_handled, queued_command_result, remote_enable_result, server_skill, server_skill_list, session_auth_status, 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_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_log_level, session_mode, sessions_fork_request, sessions_fork_result, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, task_agent_info, task_agent_info_execution_mode, task_agent_info_status, task_info, task_list, tasks_cancel_request, tasks_cancel_result, task_shell_info, task_shell_info_attachment_mode, task_shell_info_execution_mode, task_shell_info_status, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, tool, tool_list, tools_list_request, 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_handle_pending_elicitation_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, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_files_result, workspaces_read_file_request, workspaces_read_file_result) def to_dict(self) -> dict: result: dict = {} @@ -6249,6 +6358,8 @@ def to_dict(self) -> dict: result["AuthInfoType"] = to_enum(AuthInfoType, self.auth_info_type) result["CommandsHandlePendingCommandRequest"] = to_class(CommandsHandlePendingCommandRequest, self.commands_handle_pending_command_request) result["CommandsHandlePendingCommandResult"] = to_class(CommandsHandlePendingCommandResult, self.commands_handle_pending_command_result) + result["CommandsRespondToQueuedCommandRequest"] = to_class(CommandsRespondToQueuedCommandRequest, self.commands_respond_to_queued_command_request) + result["CommandsRespondToQueuedCommandResult"] = to_class(CommandsRespondToQueuedCommandResult, self.commands_respond_to_queued_command_result) result["ConnectRequest"] = to_class(ConnectRequest, self.connect_request) result["ConnectResult"] = to_class(ConnectResult, self.connect_result) result["CurrentModel"] = to_class(CurrentModel, self.current_model) @@ -6371,6 +6482,9 @@ def to_dict(self) -> dict: result["PlanUpdateRequest"] = to_class(PlanUpdateRequest, self.plan_update_request) result["Plugin"] = to_class(Plugin, self.plugin) result["PluginList"] = to_class(PluginList, self.plugin_list) + result["QueuedCommandHandled"] = to_class(QueuedCommandHandled, self.queued_command_handled) + result["QueuedCommandNotHandled"] = to_class(QueuedCommandNotHandled, self.queued_command_not_handled) + result["QueuedCommandResult"] = to_class(QueuedCommandResult, self.queued_command_result) result["RemoteEnableResult"] = to_class(RemoteEnableResult, self.remote_enable_result) result["ServerSkill"] = to_class(ServerSkill, self.server_skill) result["ServerSkillList"] = to_class(ServerSkillList, self.server_skill_list) @@ -6919,6 +7033,11 @@ async def handle_pending_command(self, params: CommandsHandlePendingCommandReque params_dict["sessionId"] = self._session_id return CommandsHandlePendingCommandResult.from_dict(await self._client.request("session.commands.handlePendingCommand", params_dict, **_timeout_kwargs(timeout))) + async def respond_to_queued_command(self, params: CommandsRespondToQueuedCommandRequest, *, timeout: float | None = None) -> CommandsRespondToQueuedCommandResult: + 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 CommandsRespondToQueuedCommandResult.from_dict(await self._client.request("session.commands.respondToQueuedCommand", params_dict, **_timeout_kwargs(timeout))) + class UiApi: def __init__(self, client: "JsonRpcClient", session_id: str): diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index b55bd921a..4f3e791ce 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -300,8 +300,11 @@ class AssistantMessageData: "Assistant response containing text content, optional tool requests, and interaction metadata" content: str message_id: str + anthropic_advisor_blocks: list[Any] | None = None + anthropic_advisor_model: str | None = None encrypted_content: str | None = None interaction_id: str | None = None + model: str | None = None output_tokens: float | None = None # Deprecated: this field is deprecated. parent_tool_call_id: str | None = None @@ -317,8 +320,11 @@ def from_dict(obj: Any) -> "AssistantMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) message_id = from_str(obj.get("messageId")) + anthropic_advisor_blocks = from_union([from_none, lambda x: from_list(lambda x: x, x)], obj.get("anthropicAdvisorBlocks")) + anthropic_advisor_model = from_union([from_none, from_str], obj.get("anthropicAdvisorModel")) encrypted_content = from_union([from_none, from_str], obj.get("encryptedContent")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) + model = from_union([from_none, from_str], obj.get("model")) output_tokens = from_union([from_none, from_float], obj.get("outputTokens")) parent_tool_call_id = from_union([from_none, from_str], obj.get("parentToolCallId")) phase = from_union([from_none, from_str], obj.get("phase")) @@ -330,8 +336,11 @@ def from_dict(obj: Any) -> "AssistantMessageData": return AssistantMessageData( content=content, message_id=message_id, + anthropic_advisor_blocks=anthropic_advisor_blocks, + anthropic_advisor_model=anthropic_advisor_model, encrypted_content=encrypted_content, interaction_id=interaction_id, + model=model, output_tokens=output_tokens, parent_tool_call_id=parent_tool_call_id, phase=phase, @@ -346,10 +355,16 @@ def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) result["messageId"] = from_str(self.message_id) + if self.anthropic_advisor_blocks is not None: + result["anthropicAdvisorBlocks"] = from_union([from_none, lambda x: from_list(lambda x: x, x)], self.anthropic_advisor_blocks) + if self.anthropic_advisor_model is not None: + result["anthropicAdvisorModel"] = from_union([from_none, from_str], self.anthropic_advisor_model) if self.encrypted_content is not None: result["encryptedContent"] = from_union([from_none, from_str], self.encrypted_content) if self.interaction_id is not None: result["interactionId"] = from_union([from_none, from_str], self.interaction_id) + if self.model is not None: + result["model"] = from_union([from_none, from_str], self.model) if self.output_tokens is not None: result["outputTokens"] = from_union([from_none, to_float], self.output_tokens) if self.parent_tool_call_id is not None: @@ -4542,6 +4557,8 @@ class UserToolSessionApproval: "The approval to add as a session-scoped rule" kind: UserToolSessionApprovalKind command_identifiers: list[str] | None = None + extension_name: str | None = None + operation: str | None = None server_name: str | None = None tool_name: str | None = None @@ -4550,11 +4567,15 @@ def from_dict(obj: Any) -> "UserToolSessionApproval": assert isinstance(obj, dict) kind = parse_enum(UserToolSessionApprovalKind, obj.get("kind")) command_identifiers = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("commandIdentifiers")) + extension_name = from_union([from_none, from_str], obj.get("extensionName")) + operation = from_union([from_none, from_str], obj.get("operation")) server_name = from_union([from_none, from_str], obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) return UserToolSessionApproval( kind=kind, command_identifiers=command_identifiers, + extension_name=extension_name, + operation=operation, server_name=server_name, tool_name=tool_name, ) @@ -4564,6 +4585,10 @@ def to_dict(self) -> dict: result["kind"] = to_enum(UserToolSessionApprovalKind, self.kind) if self.command_identifiers is not None: result["commandIdentifiers"] = from_union([from_none, lambda x: from_list(from_str, x)], self.command_identifiers) + if self.extension_name is not None: + result["extensionName"] = from_union([from_none, from_str], self.extension_name) + if self.operation is not None: + result["operation"] = from_union([from_none, from_str], self.operation) if self.server_name is not None: result["serverName"] = from_union([from_none, from_str], self.server_name) if self.tool_name is not None: @@ -4854,6 +4879,8 @@ class UserToolSessionApprovalKind(Enum): MCP = "mcp" MEMORY = "memory" CUSTOM_TOOL = "custom-tool" + EXTENSION_MANAGEMENT = "extension-management" + EXTENSION_PERMISSION_ACCESS = "extension-permission-access" class WorkingDirectoryContextHostType(Enum): diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index fc5deb6c5..1c5ca509d 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -130,6 +130,9 @@ pub mod rpc_methods { pub const SESSION_TOOLS_HANDLEPENDINGTOOLCALL: &str = "session.tools.handlePendingToolCall"; /// `session.commands.handlePendingCommand` pub const SESSION_COMMANDS_HANDLEPENDINGCOMMAND: &str = "session.commands.handlePendingCommand"; + /// `session.commands.respondToQueuedCommand` + pub const SESSION_COMMANDS_RESPONDTOQUEUEDCOMMAND: &str = + "session.commands.respondToQueuedCommand"; /// `session.ui.elicitation` pub const SESSION_UI_ELICITATION: &str = "session.ui.elicitation"; /// `session.ui.handlePendingElicitation` @@ -283,6 +286,22 @@ pub struct CommandsHandlePendingCommandResult { pub success: bool, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CommandsRespondToQueuedCommandRequest { + /// Request ID from the queued command event + pub request_id: RequestId, + /// Result of the queued command execution + pub result: serde_json::Value, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CommandsRespondToQueuedCommandResult { + /// Whether the response was accepted (false if the requestId was not found or already resolved) + pub success: bool, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ConnectRequest { @@ -1316,6 +1335,23 @@ pub struct PluginList { pub plugins: Vec, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QueuedCommandHandled { + /// The command was handled + pub handled: bool, + /// If true, stop processing remaining queued items + #[serde(skip_serializing_if = "Option::is_none")] + pub stop_processing_queue: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct QueuedCommandNotHandled { + /// The command was not handled + pub handled: bool, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RemoteEnableResult { @@ -2209,8 +2245,6 @@ pub struct WorkspacesGetWorkspaceResultWorkspace { pub remote_steerable: Option, #[serde(skip_serializing_if = "Option::is_none")] pub repository: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub summary: Option, #[serde(rename = "summary_count", skip_serializing_if = "Option::is_none")] pub summary_count: Option, #[serde(rename = "updated_at", skip_serializing_if = "Option::is_none")] @@ -2418,8 +2452,6 @@ pub struct SessionWorkspacesGetWorkspaceResultWorkspace { pub remote_steerable: Option, #[serde(skip_serializing_if = "Option::is_none")] pub repository: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub summary: Option, #[serde(rename = "summary_count", skip_serializing_if = "Option::is_none")] pub summary_count: Option, #[serde(rename = "updated_at", skip_serializing_if = "Option::is_none")] @@ -2684,6 +2716,13 @@ pub struct SessionCommandsHandlePendingCommandResult { pub success: bool, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SessionCommandsRespondToQueuedCommandResult { + /// Whether the response was accepted (false if the requestId was not found or already resolved) + pub success: bool, +} + /// The elicitation response (accept with form values, decline, or cancel) #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index ec958708c..5a0b48434 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -664,6 +664,24 @@ impl<'a> SessionRpcCommands<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + /// Wire method: `session.commands.respondToQueuedCommand`. + pub async fn respond_to_queued_command( + &self, + params: CommandsRespondToQueuedCommandRequest, + ) -> 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_COMMANDS_RESPONDTOQUEUEDCOMMAND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.extensions.*` RPCs. diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs index d27ac5a62..85c523940 100644 --- a/rust/src/generated/session_events.rs +++ b/rust/src/generated/session_events.rs @@ -1089,6 +1089,12 @@ pub struct AssistantMessageToolRequest { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AssistantMessageData { + /// Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping + #[serde(default)] + pub anthropic_advisor_blocks: Vec, + /// Anthropic advisor model ID used for this response, for timeline display on replay + #[serde(skip_serializing_if = "Option::is_none")] + pub anthropic_advisor_model: Option, /// The assistant's text response content pub content: String, /// Encrypted reasoning content from OpenAI models. Session-bound and stripped on resume. @@ -1099,6 +1105,9 @@ pub struct AssistantMessageData { pub interaction_id: Option, /// Unique identifier for this assistant message pub message_id: String, + /// Model that produced this assistant message, if known + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, /// Actual output token count from the API response (completion_tokens), used for accurate token accounting #[serde(skip_serializing_if = "Option::is_none")] pub output_tokens: Option, @@ -2098,6 +2107,25 @@ pub struct UserToolSessionApprovalCustomTool { pub tool_name: String, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserToolSessionApprovalExtensionManagement { + /// Extension management approval kind + pub kind: UserToolSessionApprovalExtensionManagementKind, + /// Optional operation identifier + #[serde(skip_serializing_if = "Option::is_none")] + pub operation: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserToolSessionApprovalExtensionPermissionAccess { + /// Extension name + pub extension_name: String, + /// Extension permission access approval kind + pub kind: UserToolSessionApprovalExtensionPermissionAccessKind, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PermissionApprovedForSession { @@ -3042,6 +3070,20 @@ pub enum UserToolSessionApprovalCustomToolKind { CustomTool, } +/// Extension management approval kind +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum UserToolSessionApprovalExtensionManagementKind { + #[serde(rename = "extension-management")] + ExtensionManagement, +} + +/// Extension permission access approval kind +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum UserToolSessionApprovalExtensionPermissionAccessKind { + #[serde(rename = "extension-permission-access")] + ExtensionPermissionAccess, +} + /// The approval to add as a session-scoped rule #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -3052,6 +3094,8 @@ pub enum UserToolSessionApproval { Mcp(UserToolSessionApprovalMcp), Memory(UserToolSessionApprovalMemory), CustomTool(UserToolSessionApprovalCustomTool), + ExtensionManagement(UserToolSessionApprovalExtensionManagement), + ExtensionPermissionAccess(UserToolSessionApprovalExtensionPermissionAccess), } /// Approved and remembered for the rest of the session diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index 69a491670..2b06abfa4 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.44-3", + "@github/copilot": "^1.0.45", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", @@ -464,27 +464,27 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.44-3.tgz", - "integrity": "sha512-hTsNxnmtKDK3ymh+c6LrsXWc9TbbubUHSxPuAKc4CX0d1c9iI1R4ybzS5Ihe+GxlozHIyFANd58gAg3QH3uCkA==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.45.tgz", + "integrity": "sha512-2QADgQcw/d0GFqTq2+nHwX152ZRvZxW0CHONG5d1RCs6YJtdr/GdbnMYYeRH2BiBIhnfkcvF50ImCRvsS5Tnwg==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.44-3", - "@github/copilot-darwin-x64": "1.0.44-3", - "@github/copilot-linux-arm64": "1.0.44-3", - "@github/copilot-linux-x64": "1.0.44-3", - "@github/copilot-win32-arm64": "1.0.44-3", - "@github/copilot-win32-x64": "1.0.44-3" + "@github/copilot-darwin-arm64": "1.0.45", + "@github/copilot-darwin-x64": "1.0.45", + "@github/copilot-linux-arm64": "1.0.45", + "@github/copilot-linux-x64": "1.0.45", + "@github/copilot-win32-arm64": "1.0.45", + "@github/copilot-win32-x64": "1.0.45" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.44-3.tgz", - "integrity": "sha512-59IXG1lGCf0Ni4TjNL6bqBul6G2FPFX2vh6pMnoRVtHvRrtFILIBMNRMNQFrYZo3eXYBqYXwVHu4R8zfELpK6A==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.45.tgz", + "integrity": "sha512-gCJy1nOIWL5lpLFJTRk2Kz7bS30emkA4p4gM+PJ5/dOwNRBOyUO0/2f03/m5vYL4DNd/T47cFIN6s82gISAIYQ==", "cpu": [ "arm64" ], @@ -499,9 +499,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.44-3.tgz", - "integrity": "sha512-I+aR9rBNzwn3OOd5oIDIpnUCkCtj3mL183Ml1LLUcJ3utxwxKVInckW/Jg36jSD2PhkbNX8gzq0l3dv0td6QYQ==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.45.tgz", + "integrity": "sha512-nLzC7C0i/WAY+4FukHuONBDNeKUAqBBab3n36aEdpqxVDP5h2Tbzg2yShqav2blR7KDJL7YMcYTVFxmwfQj+yQ==", "cpu": [ "x64" ], @@ -516,9 +516,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.44-3.tgz", - "integrity": "sha512-Agz4tMiM0hy9zIPPxKF0SSjMZSYuLYoGMe5KbvNEwTrAApLSrSW6k8yhlOTVCiRHEBsfh69We3LCOmc8hX8jVg==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.45.tgz", + "integrity": "sha512-MdRNZUNMrI0dpQ+DiDoZQ7AbitQp9eN7ir176Za2Kf7dkUxPwmio32yhRbBS81McU6vBw8cCzEZviwv/jc8buQ==", "cpu": [ "arm64" ], @@ -533,9 +533,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.44-3.tgz", - "integrity": "sha512-Ev5/uZKqSOr6l2tcy9Xqx354tuxo8qE42Cnnd6JynGrvVc1NpzF1Kt5eCzzjxdZiRtPo6AdDXS16oAN8CVxCrg==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.45.tgz", + "integrity": "sha512-xSRUjWA+wrSSjktJSjNtiS/47Cy0PviPejj7RUmtChsPfDJB8wW2iZ6NfpdiAomtxAz5xx4AjbjT1I4b1FqnwA==", "cpu": [ "x64" ], @@ -550,9 +550,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.44-3.tgz", - "integrity": "sha512-bV2JeRRNYTiTfqmCVeXdPpgYe8KY58diJFZdhYSQnQDowjKvRn59K0RBEYDGK8//AjN+NfaGPGikMq3CQm61cA==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.45.tgz", + "integrity": "sha512-lhcTlKs7MWMzIXv21hUSpL4aFW49jqVhNrQKaB8sYk2nzvGRJvNwTcBS1Tn5ndXlPzQ9P/p9B6B5uwwmZ1vHHw==", "cpu": [ "arm64" ], @@ -567,9 +567,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.44-3", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.44-3.tgz", - "integrity": "sha512-qR6q16UDC6bIO8cde62z0wwVweH351RzN1KZgMjBqQYUBJw521K8VK7p64XK0tQWoTG8uyCuqqu5djQq/4Ek+g==", + "version": "1.0.45", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.45.tgz", + "integrity": "sha512-XYZ983NQmooVr/n+pCnHIorBmf1hd3o1rMlSAodwG/VFlQaydGoOs1F1NntxWBoFAND+eM6N4PZfw8M8sRayfA==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index 72e06265e..c40ae2aa7 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -11,7 +11,7 @@ "test": "vitest run" }, "devDependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.45", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "@types/node-forge": "^1.3.14", From de22b5a2c0d517be466d63a1dc82147259a34b0a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 12 May 2026 00:06:47 -0400 Subject: [PATCH 2/2] Fix C# boolean discriminator generation Generate boolean-discriminated unions as flat DTOs so handled serializes as a JSON boolean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Generated/Rpc.cs | 28 +----- dotnet/test/Unit/SerializationTests.cs | 23 +++++ scripts/codegen/csharp.ts | 120 +++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 34 deletions(-) diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index c81e061c6..e643cc8ef 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -1797,26 +1797,12 @@ public sealed class CommandsRespondToQueuedCommandResult } /// Result of the queued command execution. -/// Polymorphic base type discriminated by handled. -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "handled", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(QueuedCommandResultTrue), "true")] -[JsonDerivedType(typeof(QueuedCommandResultFalse), "false")] +/// Data type discriminated by handled. public partial class QueuedCommandResult { - /// The type discriminator. + /// The boolean discriminator. [JsonPropertyName("handled")] - public virtual string Handled { get; set; } = string.Empty; -} - - -/// The true variant of . -public partial class QueuedCommandResultTrue : QueuedCommandResult -{ - /// - [JsonIgnore] - public override string Handled => "true"; + public bool Handled { get; set; } /// If true, stop processing remaining queued items. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -1824,14 +1810,6 @@ public partial class QueuedCommandResultTrue : QueuedCommandResult public bool? StopProcessingQueue { get; set; } } -/// The false variant of . -public partial class QueuedCommandResultFalse : QueuedCommandResult -{ - /// - [JsonIgnore] - public override string Handled => "false"; -} - /// RPC data type for CommandsRespondToQueuedCommand operations. internal sealed class CommandsRespondToQueuedCommandRequest { diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index 10e45fcf8..fa579ac6b 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -5,6 +5,7 @@ using Xunit; using System.Text.Json; using System.Text.Json.Serialization; +using GitHub.Copilot.SDK.Rpc; namespace GitHub.Copilot.SDK.Test.Unit; @@ -241,6 +242,28 @@ public void McpHttpServerConfig_CanSerializeOauthOptions_WithSdkOptions() Assert.Equal(3000, httpConfig.Timeout); } + [Fact] + public void QueuedCommandResult_SerializesHandledAsBoolean_WithSdkOptions() + { + var options = GetSerializerOptions(); + var original = new QueuedCommandResult + { + Handled = true, + StopProcessingQueue = false + }; + + var json = JsonSerializer.Serialize(original, options); + using var document = JsonDocument.Parse(json); + var root = document.RootElement; + Assert.Equal(JsonValueKind.True, root.GetProperty("handled").ValueKind); + Assert.Equal(JsonValueKind.False, root.GetProperty("stopProcessingQueue").ValueKind); + + var deserialized = JsonSerializer.Deserialize("""{"handled":false}""", options); + Assert.NotNull(deserialized); + Assert.False(deserialized.Handled); + Assert.Null(deserialized.StopProcessingQueue); + } + private static JsonSerializerOptions GetSerializerOptions() { var prop = typeof(CopilotClient) diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts index b624abcc3..f82a5cd03 100644 --- a/scripts/codegen/csharp.ts +++ b/scripts/codegen/csharp.ts @@ -416,10 +416,20 @@ function extractEventVariants(schema: JSONSchema7): EventVariant[] { }); } +interface DiscriminatorVariant { + value: unknown; + schema: JSONSchema7; +} + +interface DiscriminatorInfo { + property: string; + mapping: Map; +} + /** * Find a discriminator property shared by all variants in an anyOf. */ -function findDiscriminator(variants: JSONSchema7[]): { property: string; mapping: Map } | null { +function findDiscriminator(variants: JSONSchema7[]): DiscriminatorInfo | null { if (variants.length === 0) return null; const firstVariant = variants[0]; if (!firstVariant.properties) return null; @@ -429,7 +439,7 @@ function findDiscriminator(variants: JSONSchema7[]): { property: string; mapping const schema = propSchema as JSONSchema7; if (schema.const === undefined) continue; - const mapping = new Map(); + const mapping = new Map(); let isValidDiscriminator = true; for (const variant of variants) { @@ -438,7 +448,9 @@ function findDiscriminator(variants: JSONSchema7[]): { property: string; mapping if (typeof variantProp !== "object") { isValidDiscriminator = false; break; } const variantSchema = variantProp as JSONSchema7; if (variantSchema.const === undefined) { isValidDiscriminator = false; break; } - mapping.set(String(variantSchema.const), variant); + const key = String(variantSchema.const); + if (mapping.has(key)) { isValidDiscriminator = false; break; } + mapping.set(key, { value: variantSchema.const, schema: variant }); } if (isValidDiscriminator && mapping.size === variants.length) { @@ -459,6 +471,94 @@ type PropertyTypeResolver = ( enumOutput: string[] ) => string; +function isBooleanDiscriminator(discriminatorInfo: DiscriminatorInfo): boolean { + return Array.from(discriminatorInfo.mapping.values()).every((variant) => typeof variant.value === "boolean"); +} + +function escapeCSharpStringLiteral(value: string): string { + return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); +} + +function generateDiscriminatedUnionClass( + baseClassName: string, + discriminatorInfo: DiscriminatorInfo, + variants: JSONSchema7[], + knownTypes: Map, + nestedClasses: Map, + enumOutput: string[], + description?: string, + propertyResolver?: PropertyTypeResolver +): string { + if (isBooleanDiscriminator(discriminatorInfo)) { + return generateFlattenedBooleanDiscriminatedClass(baseClassName, discriminatorInfo, knownTypes, nestedClasses, enumOutput, description, propertyResolver); + } + + return generatePolymorphicClasses(baseClassName, discriminatorInfo.property, variants, knownTypes, nestedClasses, enumOutput, description, propertyResolver); +} + +function generateFlattenedBooleanDiscriminatedClass( + baseClassName: string, + discriminatorInfo: DiscriminatorInfo, + knownTypes: Map, + nestedClasses: Map, + enumOutput: string[], + description?: string, + propertyResolver?: PropertyTypeResolver +): string { + const resolver = propertyResolver ?? resolveSessionPropertyType; + const renamedBase = applyTypeRename(baseClassName); + const lines: string[] = []; + const flattenedProperties = new Map(); + const variants = Array.from(discriminatorInfo.mapping.values()).map((variant) => variant.schema); + + for (const variant of variants) { + const required = new Set(variant.required || []); + for (const [propName, propSchema] of Object.entries(variant.properties || {})) { + if (typeof propSchema !== "object" || propName === discriminatorInfo.property) continue; + + const existing = flattenedProperties.get(propName); + if (existing) { + existing.variantCount++; + if (required.has(propName)) existing.requiredCount++; + continue; + } + + flattenedProperties.set(propName, { + schema: propSchema as JSONSchema7, + requiredCount: required.has(propName) ? 1 : 0, + variantCount: 1, + }); + } + } + + lines.push(...xmlDocCommentWithFallback(description, `Data type discriminated by ${escapeXml(discriminatorInfo.property)}.`, "")); + lines.push(`public partial class ${renamedBase}`); + lines.push(`{`); + lines.push(` /// The boolean discriminator.`); + lines.push(` [JsonPropertyName("${discriminatorInfo.property}")]`); + lines.push(` public bool ${toPascalCase(discriminatorInfo.property)} { get; set; }`); + + const propertyEntries = Array.from(flattenedProperties.entries()).sort(([a], [b]) => a.localeCompare(b)); + for (const [propName, info] of propertyEntries) { + const isReq = info.variantCount === variants.length && info.requiredCount === variants.length; + const csharpName = toPascalCase(propName); + const csharpType = resolver(info.schema, renamedBase, csharpName, isReq, knownTypes, nestedClasses, enumOutput); + + lines.push(""); + lines.push(...xmlDocPropertyComment(info.schema.description, propName, " ")); + lines.push(...emitDataAnnotations(info.schema, " ")); + if (isSchemaDeprecated(info.schema)) lines.push(` [Obsolete("This member is deprecated and will be removed in a future version.")]`); + if (isDurationProperty(info.schema)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`); + if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`); + lines.push(` [JsonPropertyName("${propName}")]`); + const reqMod = isReq && !csharpType.endsWith("?") ? "required " : ""; + lines.push(` public ${reqMod}${csharpType} ${csharpName} { get; set; }`); + } + + lines.push(`}`); + return lines.join("\n"); +} + /** * Generate a polymorphic base class and derived classes for a discriminated union. */ @@ -482,9 +582,10 @@ function generatePolymorphicClasses( lines.push(` TypeDiscriminatorPropertyName = "${discriminatorProperty}",`); lines.push(` UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]`); - for (const [constValue] of discriminatorInfo.mapping) { + for (const { value } of discriminatorInfo.mapping.values()) { + const constValue = String(value); const derivedClassName = applyTypeRename(`${baseClassName}${toPascalCase(constValue)}`); - lines.push(`[JsonDerivedType(typeof(${derivedClassName}), "${constValue}")]`); + lines.push(`[JsonDerivedType(typeof(${derivedClassName}), "${escapeCSharpStringLiteral(constValue)}")]`); } lines.push(`public partial class ${renamedBase}`); @@ -495,9 +596,10 @@ function generatePolymorphicClasses( lines.push(`}`); lines.push(""); - for (const [constValue, variant] of discriminatorInfo.mapping) { + 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, variant, knownTypes, nestedClasses, enumOutput, resolver); + const derivedCode = generateDerivedClass(derivedClassName, renamedBase, discriminatorProperty, constValue, schema, knownTypes, nestedClasses, enumOutput, resolver); nestedClasses.set(derivedClassName, derivedCode); } @@ -641,7 +743,7 @@ function resolveSessionPropertyType( const hasNull = propSchema.anyOf.length > nonNull.length; const baseClassName = (propSchema.title as string) ?? `${parentClassName}${propName}`; const renamedBase = applyTypeRename(baseClassName); - const polymorphicCode = generatePolymorphicClasses(baseClassName, discriminatorInfo.property, variants, knownTypes, nestedClasses, enumOutput, propSchema.description); + const polymorphicCode = generateDiscriminatedUnionClass(baseClassName, discriminatorInfo, variants, knownTypes, nestedClasses, enumOutput, propSchema.description); nestedClasses.set(renamedBase, polymorphicCode); return isRequired && !hasNull ? renamedBase : `${renamedBase}?`; } @@ -981,7 +1083,7 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam } return result; }; - const polymorphicCode = generatePolymorphicClasses(baseClassName, discriminatorInfo.property, variants, rpcKnownTypes, nestedMap, rpcEnumOutput, schema.description, rpcPropertyResolver); + const polymorphicCode = generateDiscriminatedUnionClass(baseClassName, discriminatorInfo, variants, rpcKnownTypes, nestedMap, rpcEnumOutput, schema.description, rpcPropertyResolver); classes.push(polymorphicCode); for (const nested of nestedMap.values()) classes.push(nested); }