diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index c01d1ddcd..8b5c0a5f1 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -1180,7 +1180,7 @@ public partial class SessionRemoteSteerableChangedData /// Error details for timeline display including message and optional diagnostic information. public partial class SessionErrorData { - /// Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "query"). + /// Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query"). [JsonPropertyName("errorType")] public required string ErrorType { get; set; } @@ -2267,6 +2267,11 @@ public partial class PermissionRequestedData /// Details of the permission being requested. [JsonPropertyName("permissionRequest")] public required PermissionRequest PermissionRequest { get; set; } + + /// When true, this permission was already resolved by a permissionRequest hook and requires no client action. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolvedByHook")] + public bool? ResolvedByHook { get; set; } } /// Permission request completion notification signaling UI dismissal. @@ -2998,6 +3003,11 @@ public partial class AssistantMessageDataToolRequestsItem [JsonPropertyName("toolTitle")] public string? ToolTitle { get; set; } + /// Name of the MCP server hosting this tool, when the tool is an MCP tool. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mcpServerName")] + public string? McpServerName { get; set; } + /// Resolved intention summary describing what this specific call does. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("intentionSummary")] @@ -3989,6 +3999,9 @@ public enum PermissionCompletedDataResultKind /// The denied-by-content-exclusion-policy variant. [JsonStringEnumMemberName("denied-by-content-exclusion-policy")] DeniedByContentExclusionPolicy, + /// The denied-by-permission-request-hook variant. + [JsonStringEnumMemberName("denied-by-permission-request-hook")] + DeniedByPermissionRequestHook, } /// Elicitation mode; "form" for structured input, "url" for browser-based. Defaults to "form" when absent. diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index ae3d0c85b..3468e9b52 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -456,6 +456,9 @@ private async Task HandleBroadcastEventAsync(SessionEvent sessionEvent) if (string.IsNullOrEmpty(data.RequestId) || data.PermissionRequest is null) return; + if (data.ResolvedByHook == true) + return; // Already resolved by a permissionRequest hook; no client action needed. + var handler = _permissionHandler; if (handler is null) return; // This client doesn't handle permissions; another client will. diff --git a/go/generated_session_events.go b/go/generated_session_events.go index 4799aca91..e3b6fa71e 100644 --- a/go/generated_session_events.go +++ b/go/generated_session_events.go @@ -358,7 +358,7 @@ type Data struct { // ISO 8601 timestamp when the session was resumed ResumeTime *time.Time `json:"resumeTime,omitempty"` // Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", - // "query") + // "context_limit", "query") ErrorType *string `json:"errorType,omitempty"` // Human-readable error message // @@ -801,6 +801,9 @@ type Data struct { Kind *KindClass `json:"kind,omitempty"` // Details of the permission being requested PermissionRequest *PermissionRequest `json:"permissionRequest,omitempty"` + // When true, this permission was already resolved by a permissionRequest hook and requires + // no client action + ResolvedByHook *bool `json:"resolvedByHook,omitempty"` // Whether the user can provide a free-form text response in addition to predefined choices AllowFreeform *bool `json:"allowFreeform,omitempty"` // Predefined choices for the user to select from, if applicable @@ -1403,6 +1406,8 @@ type ToolRequest struct { Arguments interface{} `json:"arguments"` // Resolved intention summary describing what this specific call does IntentionSummary *string `json:"intentionSummary"` + // Name of the MCP server hosting this tool, when the tool is an MCP tool + MCPServerName *string `json:"mcpServerName,omitempty"` // Name of the tool being invoked Name string `json:"name"` // Unique identifier for this tool call @@ -1556,6 +1561,7 @@ type ResultKind string const ( ResultKindApproved ResultKind = "approved" ResultKindDeniedByContentExclusionPolicy ResultKind = "denied-by-content-exclusion-policy" + ResultKindDeniedByPermissionRequestHook ResultKind = "denied-by-permission-request-hook" ResultKindDeniedByRules ResultKind = "denied-by-rules" ResultKindDeniedInteractivelyByUser ResultKind = "denied-interactively-by-user" ResultKindDeniedNoApprovalRuleAndCouldNotRequestFromUser ResultKind = "denied-no-approval-rule-and-could-not-request-from-user" diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go index f6011d900..3e7b336b7 100644 --- a/go/rpc/generated_rpc.go +++ b/go/rpc/generated_rpc.go @@ -652,11 +652,12 @@ type SessionPermissionsHandlePendingPermissionRequestParams struct { } type SessionPermissionsHandlePendingPermissionRequestParamsResult struct { - Kind Kind `json:"kind"` - Rules []any `json:"rules,omitempty"` - Feedback *string `json:"feedback,omitempty"` - Message *string `json:"message,omitempty"` - Path *string `json:"path,omitempty"` + Kind Kind `json:"kind"` + Rules []any `json:"rules,omitempty"` + Feedback *string `json:"feedback,omitempty"` + Message *string `json:"message,omitempty"` + Path *string `json:"path,omitempty"` + Interrupt *bool `json:"interrupt,omitempty"` } type SessionLogResult struct { @@ -815,6 +816,7 @@ type Kind string const ( KindApproved Kind = "approved" KindDeniedByContentExclusionPolicy Kind = "denied-by-content-exclusion-policy" + KindDeniedByPermissionRequestHook Kind = "denied-by-permission-request-hook" KindDeniedByRules Kind = "denied-by-rules" KindDeniedInteractivelyByUser Kind = "denied-interactively-by-user" KindDeniedNoApprovalRuleAndCouldNotRequestFromUser Kind = "denied-no-approval-rule-and-could-not-request-from-user" diff --git a/go/session.go b/go/session.go index 04c1a05b0..225f2bf5e 100644 --- a/go/session.go +++ b/go/session.go @@ -915,6 +915,9 @@ func (s *Session) handleBroadcastEvent(event SessionEvent) { if requestID == nil || event.Data.PermissionRequest == nil { return } + if event.Data.ResolvedByHook != nil && *event.Data.ResolvedByHook { + return // Already resolved by a permissionRequest hook; no client action needed. + } handler := s.getPermissionHandler() if handler == nil { return diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 1af6e76c6..98ed1f0c7 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.15-2", + "@github/copilot": "^1.0.17", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, @@ -663,26 +663,26 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.15-2.tgz", - "integrity": "sha512-ZVwGAH9u55CbGsM2fbZr9yL7oML5NZxfMbATBU9hWY8yEjiaSj+9WkRPxCSxGsd2cu4tw3OcHhFkDvxvWd2QpQ==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.17.tgz", + "integrity": "sha512-RTJ+kEKOdidjuOs8ozsoBdz+94g7tFJIEu5kz1P2iwJhsL+iIA5rtn9/jXOF0hAI3CLSXKZoSd66cqHrn4rb1A==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.15-2", - "@github/copilot-darwin-x64": "1.0.15-2", - "@github/copilot-linux-arm64": "1.0.15-2", - "@github/copilot-linux-x64": "1.0.15-2", - "@github/copilot-win32-arm64": "1.0.15-2", - "@github/copilot-win32-x64": "1.0.15-2" + "@github/copilot-darwin-arm64": "1.0.17", + "@github/copilot-darwin-x64": "1.0.17", + "@github/copilot-linux-arm64": "1.0.17", + "@github/copilot-linux-x64": "1.0.17", + "@github/copilot-win32-arm64": "1.0.17", + "@github/copilot-win32-x64": "1.0.17" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.15-2.tgz", - "integrity": "sha512-J2kvPBbNC636z3YdFdg2uK8YAF0o1ktss4Cmz+WVi5+5rNzscty3GmUoWBgw1AtPRNSeFT8amMVZ9xBvkpzA/A==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.17.tgz", + "integrity": "sha512-LSv66P8611y/UjTESnaHLYqLl9kA9yBYsaocZPQoOsvMgCmktgaBgUWq+KMpLMicaFN0jBAE5F0Ve7dW6N9X3A==", "cpu": [ "arm64" ], @@ -696,9 +696,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.15-2.tgz", - "integrity": "sha512-utoHP7RyJXasNVQtpAhkDfp4jTLiNwJf5ZFjOkb9XMASre0+i4CfsokuXb1yPXczXFnrLcreVWQ2wtSuRiyV3A==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.17.tgz", + "integrity": "sha512-yqRS0/8kYTGl4VvfJ/QOtHTeYF+DnAWNUReZgt2U0AEP3zgj4z4hxSH7D2PsO/488L4KsBmmcnJr13HmBGiT/w==", "cpu": [ "x64" ], @@ -712,9 +712,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.15-2.tgz", - "integrity": "sha512-tkqt6W+3VhZRvTMQoNj80s5JWNu5TXPYnNQkrPzAviqTsd8BRXOSGnqcIL7DvU+Y0z4pY5IS0ZECByB0IsRSHw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.17.tgz", + "integrity": "sha512-TOK0ma0A24zmQJslkGxUk+KnMFpiqquWEXB5sIv/5Ci45Qi7s0BRWTnqtiJ8Vahwb/wkja6KarHkLA27+ETGUA==", "cpu": [ "arm64" ], @@ -728,9 +728,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.15-2.tgz", - "integrity": "sha512-svGfox/x8pNzrxcTAkpbqyWzaeQiJaRj6ZuQzzGJGi5+G6kAok3iqIInO+QYNB6fozW8oLnR8QJigAoj8Ldzbw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.17.tgz", + "integrity": "sha512-4Yum3uaAuTM/SiNtzchsO/G/144Bi/Z4FEcearW6WsGDvS6cRwSJeudOM0y4aoy4BHcv8+yw7YuXH5BHC3SAiA==", "cpu": [ "x64" ], @@ -744,9 +744,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.15-2.tgz", - "integrity": "sha512-ZM/cmICtOOknMLkN42OvCRaLp5qJPBN9GAKkwTWCrhBmFpAIjC9O679AQA6KiCNj4OUzL6Hi5mSl9ufdUzPwkw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.17.tgz", + "integrity": "sha512-I1ferbfQ0aS149WyEUw6XS1sFixwTUUm13BPBQ3yMzD8G2SaoxTsdYdlhZpkVfkfh/rUYyvMKKi9VNxoVYOlDA==", "cpu": [ "arm64" ], @@ -760,9 +760,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.15-2", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.15-2.tgz", - "integrity": "sha512-tAyd3Fzta6XJoH5MZ3yaw4H8i92C6k0zVkLKzL5zhrm4YEGWyQMcGB7NlLcvcmKewx49smCjbWtO/TIcVWJrrA==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.17.tgz", + "integrity": "sha512-kjiOxY9ibS+rPp9XFpPdfdYzluEL3SHN8R5/fnA7RO+kZEJ4FDKWJjAiec3tgVkEHQT3UwNuVa/u3TdfYNF15w==", "cpu": [ "x64" ], diff --git a/nodejs/package.json b/nodejs/package.json index ce8d99a86..99681ec3f 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -56,7 +56,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.15-2", + "@github/copilot": "^1.0.17", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, diff --git a/nodejs/samples/package-lock.json b/nodejs/samples/package-lock.json index cd2ce2305..c0749ee6c 100644 --- a/nodejs/samples/package-lock.json +++ b/nodejs/samples/package-lock.json @@ -18,11 +18,12 @@ "version": "0.1.8", "license": "MIT", "dependencies": { - "@github/copilot": "^1.0.11", + "@github/copilot": "^1.0.17", "vscode-jsonrpc": "^8.2.1", "zod": "^4.3.6" }, "devDependencies": { + "@platformatic/vfs": "^0.3.0", "@types/node": "^25.2.0", "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 845d49129..4f87c14f2 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -1185,6 +1185,11 @@ export interface SessionPermissionsHandlePendingPermissionRequestParams { kind: "denied-by-content-exclusion-policy"; path: string; message: string; + } + | { + kind: "denied-by-permission-request-hook"; + message?: string; + interrupt?: boolean; }; } diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts index 137c474f2..642c933cd 100644 --- a/nodejs/src/generated/session-events.ts +++ b/nodejs/src/generated/session-events.ts @@ -229,7 +229,7 @@ export type SessionEvent = */ data: { /** - * Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "query") + * Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") */ errorType: string; /** @@ -1480,6 +1480,10 @@ export type SessionEvent = * Human-readable display title for the tool */ toolTitle?: string; + /** + * Name of the MCP server hosting this tool, when the tool is an MCP tool + */ + mcpServerName?: string; /** * Resolved intention summary describing what this specific call does */ @@ -2872,6 +2876,10 @@ export type SessionEvent = */ hookMessage?: string; }; + /** + * When true, this permission was already resolved by a permissionRequest hook and requires no client action + */ + resolvedByHook?: boolean; }; } | { @@ -2909,7 +2917,8 @@ export type SessionEvent = | "denied-by-rules" | "denied-no-approval-rule-and-could-not-request-from-user" | "denied-interactively-by-user" - | "denied-by-content-exclusion-policy"; + | "denied-by-content-exclusion-policy" + | "denied-by-permission-request-hook"; }; }; } diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts index 50f094e5a..0bd5ad7b8 100644 --- a/nodejs/src/session.ts +++ b/nodejs/src/session.ts @@ -408,10 +408,14 @@ export class CopilotSession { ); } } else if (event.type === "permission.requested") { - const { requestId, permissionRequest } = event.data as { + const { requestId, permissionRequest, resolvedByHook } = event.data as { requestId: string; permissionRequest: PermissionRequest; + resolvedByHook?: boolean; }; + if (resolvedByHook) { + return; // Already resolved by a permissionRequest hook; no client action needed. + } if (this.permissionHandler) { void this._executePermissionAndRespond(requestId, permissionRequest); } diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 39e20a05d..7852d9984 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -2257,6 +2257,7 @@ def to_dict(self) -> dict: class Kind(Enum): APPROVED = "approved" DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" + DENIED_BY_PERMISSION_REQUEST_HOOK = "denied-by-permission-request-hook" DENIED_BY_RULES = "denied-by-rules" DENIED_INTERACTIVELY_BY_USER = "denied-interactively-by-user" DENIED_NO_APPROVAL_RULE_AND_COULD_NOT_REQUEST_FROM_USER = "denied-no-approval-rule-and-could-not-request-from-user" @@ -2269,6 +2270,7 @@ class SessionPermissionsHandlePendingPermissionRequestParamsResult: feedback: str | None = None message: str | None = None path: str | None = None + interrupt: bool | None = None @staticmethod def from_dict(obj: Any) -> 'SessionPermissionsHandlePendingPermissionRequestParamsResult': @@ -2278,7 +2280,8 @@ def from_dict(obj: Any) -> 'SessionPermissionsHandlePendingPermissionRequestPara feedback = from_union([from_str, from_none], obj.get("feedback")) message = from_union([from_str, from_none], obj.get("message")) path = from_union([from_str, from_none], obj.get("path")) - return SessionPermissionsHandlePendingPermissionRequestParamsResult(kind, rules, feedback, message, path) + interrupt = from_union([from_bool, from_none], obj.get("interrupt")) + return SessionPermissionsHandlePendingPermissionRequestParamsResult(kind, rules, feedback, message, path, interrupt) def to_dict(self) -> dict: result: dict = {} @@ -2291,6 +2294,8 @@ def to_dict(self) -> dict: result["message"] = from_union([from_str, from_none], self.message) if self.path is not None: result["path"] = from_union([from_str, from_none], self.path) + if self.interrupt is not None: + result["interrupt"] = from_union([from_bool, from_none], self.interrupt) return result diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 2c3acba81..9b4267829 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -1500,6 +1500,7 @@ class ResultKind(Enum): APPROVED = "approved" DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" + DENIED_BY_PERMISSION_REQUEST_HOOK = "denied-by-permission-request-hook" DENIED_BY_RULES = "denied-by-rules" DENIED_INTERACTIVELY_BY_USER = "denied-interactively-by-user" DENIED_NO_APPROVAL_RULE_AND_COULD_NOT_REQUEST_FROM_USER = "denied-no-approval-rule-and-could-not-request-from-user" @@ -1708,6 +1709,9 @@ class ToolRequest: intention_summary: str | None = None """Resolved intention summary describing what this specific call does""" + mcp_server_name: str | None = None + """Name of the MCP server hosting this tool, when the tool is an MCP tool""" + tool_title: str | None = None """Human-readable display title for the tool""" @@ -1723,9 +1727,10 @@ def from_dict(obj: Any) -> 'ToolRequest': tool_call_id = from_str(obj.get("toolCallId")) arguments = obj.get("arguments") intention_summary = from_union([from_none, from_str], obj.get("intentionSummary")) + mcp_server_name = from_union([from_str, from_none], obj.get("mcpServerName")) tool_title = from_union([from_str, from_none], obj.get("toolTitle")) type = from_union([ToolRequestType, from_none], obj.get("type")) - return ToolRequest(name, tool_call_id, arguments, intention_summary, tool_title, type) + return ToolRequest(name, tool_call_id, arguments, intention_summary, mcp_server_name, tool_title, type) def to_dict(self) -> dict: result: dict = {} @@ -1735,6 +1740,8 @@ def to_dict(self) -> dict: result["arguments"] = self.arguments if self.intention_summary is not None: result["intentionSummary"] = from_union([from_none, from_str], self.intention_summary) + if self.mcp_server_name is not None: + result["mcpServerName"] = from_union([from_str, from_none], self.mcp_server_name) if self.tool_title is not None: result["toolTitle"] = from_union([from_str, from_none], self.tool_title) if self.type is not None: @@ -1957,7 +1964,7 @@ class Data: error_type: str | None = None """Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", - "query") + "context_limit", "query") """ message: str | None = None """Human-readable error message @@ -2527,6 +2534,10 @@ class Data: permission_request: PermissionRequest | None = None """Details of the permission being requested""" + resolved_by_hook: bool | None = None + """When true, this permission was already resolved by a permissionRequest hook and requires + no client action + """ allow_freeform: bool | None = None """Whether the user can provide a free-form text response in addition to predefined choices""" @@ -2758,6 +2769,7 @@ def from_dict(obj: Any) -> 'Data': role = from_union([Role, from_none], obj.get("role")) kind = from_union([KindClass.from_dict, from_none], obj.get("kind")) permission_request = from_union([PermissionRequest.from_dict, from_none], obj.get("permissionRequest")) + resolved_by_hook = from_union([from_bool, from_none], obj.get("resolvedByHook")) allow_freeform = from_union([from_bool, from_none], obj.get("allowFreeform")) choices = from_union([lambda x: from_list(from_str, x), from_none], obj.get("choices")) question = from_union([from_str, from_none], obj.get("question")) @@ -2785,7 +2797,7 @@ def from_dict(obj: Any) -> 'Data': servers = from_union([lambda x: from_list(Server.from_dict, x), from_none], obj.get("servers")) status = from_union([ServerStatus, from_none], obj.get("status")) extensions = from_union([lambda x: from_list(Extension.from_dict, x), from_none], obj.get("extensions")) - return Data(already_in_use, context, copilot_version, producer, reasoning_effort, remote_steerable, selected_model, session_id, start_time, version, event_count, resume_time, error_type, message, provider_call_id, stack, status_code, url, aborted, background_tasks, title, info_type, warning_type, new_model, previous_model, previous_reasoning_effort, new_mode, previous_mode, operation, path, handoff_time, host, remote_session_id, repository, source_type, summary, messages_removed_during_truncation, performed_by, post_truncation_messages_length, post_truncation_tokens_in_messages, pre_truncation_messages_length, pre_truncation_tokens_in_messages, token_limit, tokens_removed_during_truncation, events_removed, up_to_event_id, code_changes, conversation_tokens, current_model, current_tokens, error_reason, model_metrics, session_start_time, shutdown_type, system_tokens, tool_definitions_tokens, total_api_duration_ms, total_premium_requests, base_commit, branch, cwd, git_root, head_commit, host_type, is_initial, messages_length, checkpoint_number, checkpoint_path, compaction_tokens_used, error, messages_removed, post_compaction_tokens, pre_compaction_messages_length, pre_compaction_tokens, request_id, success, summary_content, tokens_removed, agent_mode, attachments, content, interaction_id, source, transformed_content, turn_id, intent, reasoning_id, delta_content, total_response_size_bytes, encrypted_content, message_id, output_tokens, parent_tool_call_id, phase, reasoning_opaque, reasoning_text, tool_requests, api_call_id, cache_read_tokens, cache_write_tokens, copilot_usage, cost, duration, initiator, input_tokens, inter_token_latency_ms, model, quota_snapshots, ttft_ms, reason, arguments, tool_call_id, tool_name, mcp_server_name, mcp_tool_name, partial_output, progress_message, is_user_requested, result, tool_telemetry, allowed_tools, description, name, plugin_name, plugin_version, agent_description, agent_display_name, agent_name, duration_ms, total_tokens, total_tool_calls, tools, hook_invocation_id, hook_type, input, output, metadata, role, kind, permission_request, allow_freeform, choices, question, elicitation_source, mode, requested_schema, mcp_request_id, server_name, server_url, static_client_config, traceparent, tracestate, command, args, command_name, commands, ui, actions, plan_content, recommended_action, skills, agents, errors, warnings, servers, status, extensions) + return Data(already_in_use, context, copilot_version, producer, reasoning_effort, remote_steerable, selected_model, session_id, start_time, version, event_count, resume_time, error_type, message, provider_call_id, stack, status_code, url, aborted, background_tasks, title, info_type, warning_type, new_model, previous_model, previous_reasoning_effort, new_mode, previous_mode, operation, path, handoff_time, host, remote_session_id, repository, source_type, summary, messages_removed_during_truncation, performed_by, post_truncation_messages_length, post_truncation_tokens_in_messages, pre_truncation_messages_length, pre_truncation_tokens_in_messages, token_limit, tokens_removed_during_truncation, events_removed, up_to_event_id, code_changes, conversation_tokens, current_model, current_tokens, error_reason, model_metrics, session_start_time, shutdown_type, system_tokens, tool_definitions_tokens, total_api_duration_ms, total_premium_requests, base_commit, branch, cwd, git_root, head_commit, host_type, is_initial, messages_length, checkpoint_number, checkpoint_path, compaction_tokens_used, error, messages_removed, post_compaction_tokens, pre_compaction_messages_length, pre_compaction_tokens, request_id, success, summary_content, tokens_removed, agent_mode, attachments, content, interaction_id, source, transformed_content, turn_id, intent, reasoning_id, delta_content, total_response_size_bytes, encrypted_content, message_id, output_tokens, parent_tool_call_id, phase, reasoning_opaque, reasoning_text, tool_requests, api_call_id, cache_read_tokens, cache_write_tokens, copilot_usage, cost, duration, initiator, input_tokens, inter_token_latency_ms, model, quota_snapshots, ttft_ms, reason, arguments, tool_call_id, tool_name, mcp_server_name, mcp_tool_name, partial_output, progress_message, is_user_requested, result, tool_telemetry, allowed_tools, description, name, plugin_name, plugin_version, agent_description, agent_display_name, agent_name, duration_ms, total_tokens, total_tool_calls, tools, hook_invocation_id, hook_type, input, output, metadata, role, kind, permission_request, resolved_by_hook, allow_freeform, choices, question, elicitation_source, mode, requested_schema, mcp_request_id, server_name, server_url, static_client_config, traceparent, tracestate, command, args, command_name, commands, ui, actions, plan_content, recommended_action, skills, agents, errors, warnings, servers, status, extensions) def to_dict(self) -> dict: result: dict = {} @@ -3069,6 +3081,8 @@ def to_dict(self) -> dict: result["kind"] = from_union([lambda x: to_class(KindClass, x), from_none], self.kind) if self.permission_request is not None: result["permissionRequest"] = from_union([lambda x: to_class(PermissionRequest, x), from_none], self.permission_request) + if self.resolved_by_hook is not None: + result["resolvedByHook"] = from_union([from_bool, from_none], self.resolved_by_hook) if self.allow_freeform is not None: result["allowFreeform"] = from_union([from_bool, from_none], self.allow_freeform) if self.choices is not None: diff --git a/python/copilot/session.py b/python/copilot/session.py index 96bb4730b..9bf384fbe 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -1224,6 +1224,10 @@ def _handle_broadcast_event(self, event: SessionEvent) -> None: if not request_id or not permission_request: return + resolved_by_hook = getattr(event.data, "resolved_by_hook", None) + if resolved_by_hook: + return # Already resolved by a permissionRequest hook; no client action needed. + with self._permission_handler_lock: perm_handler = self._permission_handler if not perm_handler: diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json index d1ee2fa24..5d055e680 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.14-0", + "@github/copilot": "^1.0.17", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0", @@ -462,27 +462,27 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.14-0.tgz", - "integrity": "sha512-9eA5sFbvx69OtQnVoeik/8boFqHgGAhylLeUjEACc3kB70aaH1E/cHgxNzSMyYgZDjpXov0/IBXjtx2otpfHBw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.17.tgz", + "integrity": "sha512-RTJ+kEKOdidjuOs8ozsoBdz+94g7tFJIEu5kz1P2iwJhsL+iIA5rtn9/jXOF0hAI3CLSXKZoSd66cqHrn4rb1A==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.14-0", - "@github/copilot-darwin-x64": "1.0.14-0", - "@github/copilot-linux-arm64": "1.0.14-0", - "@github/copilot-linux-x64": "1.0.14-0", - "@github/copilot-win32-arm64": "1.0.14-0", - "@github/copilot-win32-x64": "1.0.14-0" + "@github/copilot-darwin-arm64": "1.0.17", + "@github/copilot-darwin-x64": "1.0.17", + "@github/copilot-linux-arm64": "1.0.17", + "@github/copilot-linux-x64": "1.0.17", + "@github/copilot-win32-arm64": "1.0.17", + "@github/copilot-win32-x64": "1.0.17" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.14-0.tgz", - "integrity": "sha512-w11Eqmfnu0ihrvgLysTd5Tkq8LuQa9eW63CNTQ/k5copnG1AMCdvd3K/78MxE2DdFJPq2L95KGS5cs9jH1dlIw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.17.tgz", + "integrity": "sha512-LSv66P8611y/UjTESnaHLYqLl9kA9yBYsaocZPQoOsvMgCmktgaBgUWq+KMpLMicaFN0jBAE5F0Ve7dW6N9X3A==", "cpu": [ "arm64" ], @@ -497,9 +497,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.14-0.tgz", - "integrity": "sha512-4X/dMSPxCE/rvL6N1tgnwFxBg2uXnPrN63GGgS/FqK/fNi3TtcuojDVv8K1yjmEYpF8PXdkQttDlp6bKc+Nonw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.17.tgz", + "integrity": "sha512-yqRS0/8kYTGl4VvfJ/QOtHTeYF+DnAWNUReZgt2U0AEP3zgj4z4hxSH7D2PsO/488L4KsBmmcnJr13HmBGiT/w==", "cpu": [ "x64" ], @@ -514,9 +514,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.14-0.tgz", - "integrity": "sha512-A4thcLUoErEvfBO3Hsl/hJASibn44qwZm1ZSeVBPCa1FkpowBwo8fT1eV9EwN/ftKsyks3QkndNFvHkVzjUfxA==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.17.tgz", + "integrity": "sha512-TOK0ma0A24zmQJslkGxUk+KnMFpiqquWEXB5sIv/5Ci45Qi7s0BRWTnqtiJ8Vahwb/wkja6KarHkLA27+ETGUA==", "cpu": [ "arm64" ], @@ -531,9 +531,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.14-0.tgz", - "integrity": "sha512-Kwn+Qn8/BqWRKa2DewZipH7rPIO8nDRWzpVy/ZLcRWBAvnIU+6BLWfhnYEU44DsqkD2VeWhKVfQlNmDX23xKKg==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.17.tgz", + "integrity": "sha512-4Yum3uaAuTM/SiNtzchsO/G/144Bi/Z4FEcearW6WsGDvS6cRwSJeudOM0y4aoy4BHcv8+yw7YuXH5BHC3SAiA==", "cpu": [ "x64" ], @@ -548,9 +548,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.14-0.tgz", - "integrity": "sha512-8P5kxcb8YVWSS+Ihs+ykyy8jov1WwQ8GKV4d7mJN268Jpd8y5VI8Peb7uE2VO0lRLgq5c2VcXuZDsLG/1Wgnlw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.17.tgz", + "integrity": "sha512-I1ferbfQ0aS149WyEUw6XS1sFixwTUUm13BPBQ3yMzD8G2SaoxTsdYdlhZpkVfkfh/rUYyvMKKi9VNxoVYOlDA==", "cpu": [ "arm64" ], @@ -565,9 +565,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.14-0", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.14-0.tgz", - "integrity": "sha512-JWxp08j5o/PUkRZtZVagNYJLjH+KCURCyZRb7BfnC0A3vLeqcJQ70JC5qlYEAlcRnb4uCUJnmnpbWLLOJ+ObrA==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.17.tgz", + "integrity": "sha512-kjiOxY9ibS+rPp9XFpPdfdYzluEL3SHN8R5/fnA7RO+kZEJ4FDKWJjAiec3tgVkEHQT3UwNuVa/u3TdfYNF15w==", "cpu": [ "x64" ], diff --git a/test/harness/package.json b/test/harness/package.json index f8fe732e4..257caf35c 100644 --- a/test/harness/package.json +++ b/test/harness/package.json @@ -11,7 +11,7 @@ "test": "vitest run" }, "devDependencies": { - "@github/copilot": "^1.0.14-0", + "@github/copilot": "^1.0.17", "@modelcontextprotocol/sdk": "^1.26.0", "@types/node": "^25.3.3", "openai": "^6.17.0",