From 55d25787029f576709da4e1352f3d0396e7f92c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 21:49:22 +0000 Subject: [PATCH 1/2] Add Permission Handling documentation to all language READMEs Co-authored-by: jamesmontemagno <1676321+jamesmontemagno@users.noreply.github.com> --- dotnet/README.md | 86 +++++++++++++++++++++++++++++++++++++++++- go/README.md | 76 ++++++++++++++++++++++++++++++++++++- nodejs/README.md | 81 ++++++++++++++++++++++++++++++++++++++-- python/README.md | 97 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 329 insertions(+), 11 deletions(-) diff --git a/dotnet/README.md b/dotnet/README.md index 712323c0c..817bb4f8c 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -28,10 +28,11 @@ using GitHub.Copilot.SDK; await using var client = new CopilotClient(); await client.StartAsync(); -// Create a session +// Create a session (OnPermissionRequest is required) await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-5" + Model = "gpt-5", + OnPermissionRequest = PermissionHandler.ApproveAll, }); // Wait for response using session.idle event @@ -110,6 +111,7 @@ Create a new conversation session. - `Provider` - Custom API provider configuration (BYOK) - `Streaming` - Enable streaming of response chunks (default: false) - `InfiniteSessions` - Configure automatic context compaction (see below) +- `OnPermissionRequest` - **Required.** Handler called before each tool execution to approve or deny it. Use `PermissionHandler.ApproveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` - Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `Hooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. @@ -117,6 +119,10 @@ Create a new conversation session. Resume an existing session. Returns the session with `WorkspacePath` populated if infinite sessions were enabled. +**ResumeSessionConfig:** + +- `OnPermissionRequest` - **Required.** Handler called before each tool execution to approve or deny it. See [Permission Handling](#permission-handling) section. + ##### `PingAsync(string? message = null): Task` Ping the server to check connectivity. @@ -573,6 +579,82 @@ Trace context (`traceparent`/`tracestate`) is automatically propagated between t No extra dependencies — uses built-in `System.Diagnostics.Activity`. +## Permission Handling + +An `OnPermissionRequest` handler is **required** whenever you create or resume a session. The handler is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and must return a decision. + +### Approve All (simplest) + +Use the built-in `PermissionHandler.ApproveAll` helper to allow every tool call without any checks: + +```csharp +using GitHub.Copilot.SDK; + +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + OnPermissionRequest = PermissionHandler.ApproveAll, +}); +``` + +### Custom Permission Handler + +Provide your own `PermissionRequestHandler` delegate to inspect each request and apply custom logic: + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + OnPermissionRequest = async (request, invocation) => + { + // request.Kind — what type of operation is being requested: + // PermissionRequestKind.Shell — executing a shell command + // PermissionRequestKind.Write — writing or editing a file + // PermissionRequestKind.Read — reading a file + // PermissionRequestKind.Mcp — calling an MCP tool + // PermissionRequestKind.CustomTool — calling one of your registered tools + // PermissionRequestKind.Url — fetching a URL + // request.ToolCallId — the tool call that triggered this request + // request.ToolName — name of the tool (for custom-tool / mcp) + // request.FileName — file being written (for write) + // request.FullCommandText — full shell command text (for shell) + + if (request.Kind == PermissionRequestKind.Shell) + { + // Deny shell commands + return new PermissionRequestResult { Kind = PermissionRequestResultKind.DeniedInteractivelyByUser }; + } + + return new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved }; + } +}); +``` + +### Permission Result Kinds + +| Value | Meaning | +|-------|---------| +| `PermissionRequestResultKind.Approved` | Allow the tool to run | +| `PermissionRequestResultKind.DeniedInteractivelyByUser` | User explicitly denied the request | +| `PermissionRequestResultKind.DeniedCouldNotRequestFromUser` | No approval rule matched and user could not be asked | +| `PermissionRequestResultKind.DeniedByRules` | Denied by a policy rule | +| `PermissionRequestResultKind.NoResult` | No decision (treated as denied) | + +### Resuming Sessions + +Pass `OnPermissionRequest` when resuming a session too — it is required: + +```csharp +var session = await client.ResumeSessionAsync("session-id", new ResumeSessionConfig +{ + OnPermissionRequest = PermissionHandler.ApproveAll, +}); +``` + +### Per-Tool Skip Permission + +To let a specific custom tool bypass the permission prompt entirely, set `skip_permission = true` in the tool's `AdditionalProperties`. See [Skipping Permission Prompts](#skipping-permission-prompts) under Tools. + ## User Input Requests Enable the agent to ask questions to the user using the `ask_user` tool by providing an `OnUserInputRequest` handler: diff --git a/go/README.md b/go/README.md index f87c3d1b8..3ad9006fd 100644 --- a/go/README.md +++ b/go/README.md @@ -44,9 +44,10 @@ func main() { } defer client.Stop() - // Create a session + // Create a session (OnPermissionRequest is required) session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ - Model: "gpt-5", + Model: "gpt-5", + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, }) if err != nil { log.Fatal(err) @@ -153,11 +154,13 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `Provider` (\*ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. - `Streaming` (bool): Enable streaming delta events - `InfiniteSessions` (\*InfiniteSessionConfig): Automatic context compaction configuration +- `OnPermissionRequest` (PermissionHandlerFunc): **Required.** Handler called before each tool execution to approve or deny it. Use `copilot.PermissionHandler.ApproveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. - `OnUserInputRequest` (UserInputHandler): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `Hooks` (\*SessionHooks): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. **ResumeSessionConfig:** +- `OnPermissionRequest` (PermissionHandlerFunc): **Required.** Handler called before each tool execution to approve or deny it. See [Permission Handling](#permission-handling) section. - `Tools` ([]Tool): Tools to expose when resuming - `ReasoningEffort` (string): Reasoning effort level for models that support it - `Provider` (\*ProviderConfig): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. @@ -499,6 +502,75 @@ Trace context (`traceparent`/`tracestate`) is automatically propagated between t Dependency: `go.opentelemetry.io/otel` +## Permission Handling + +An `OnPermissionRequest` handler is **required** whenever you create or resume a session. The handler is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and must return a decision. + +### Approve All (simplest) + +Use the built-in `PermissionHandler.ApproveAll` helper to allow every tool call without any checks: + +```go +session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ + Model: "gpt-5", + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, +}) +``` + +### Custom Permission Handler + +Provide your own `PermissionHandlerFunc` to inspect each request and apply custom logic: + +```go +session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ + Model: "gpt-5", + OnPermissionRequest: func(request copilot.PermissionRequest, invocation copilot.PermissionInvocation) (copilot.PermissionRequestResult, error) { + // request.Kind — what type of operation is being requested: + // copilot.KindShell — executing a shell command + // copilot.Write — writing or editing a file + // copilot.Read — reading a file + // copilot.MCP — calling an MCP tool + // copilot.CustomTool — calling one of your registered tools + // copilot.URL — fetching a URL + // request.ToolCallID — pointer to the tool call that triggered this request + // request.ToolName — pointer to the name of the tool (for custom-tool / mcp) + // request.FileName — pointer to the file being written (for write) + // request.FullCommandText — pointer to the full shell command (for shell) + + if request.Kind == copilot.KindShell { + // Deny shell commands + return copilot.PermissionRequestResult{Kind: copilot.PermissionRequestResultKindDeniedInteractivelyByUser}, nil + } + + return copilot.PermissionRequestResult{Kind: copilot.PermissionRequestResultKindApproved}, nil + }, +}) +``` + +### Permission Result Kinds + +| Constant | Meaning | +|----------|---------| +| `PermissionRequestResultKindApproved` | Allow the tool to run | +| `PermissionRequestResultKindDeniedInteractivelyByUser` | User explicitly denied the request | +| `PermissionRequestResultKindDeniedCouldNotRequestFromUser` | No approval rule matched and user could not be asked | +| `PermissionRequestResultKindDeniedByRules` | Denied by a policy rule | +| `PermissionRequestResultKindNoResult` | No decision (treated as denied) | + +### Resuming Sessions + +Pass `OnPermissionRequest` when resuming a session too — it is required: + +```go +session, err := client.ResumeSession(context.Background(), sessionID, &copilot.ResumeSessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, +}) +``` + +### Per-Tool Skip Permission + +To let a specific custom tool bypass the permission prompt entirely, set `SkipPermission = true` on the tool. See [Skipping Permission Prompts](#skipping-permission-prompts) under Tools. + ## User Input Requests Enable the agent to ask questions to the user using the `ask_user` tool by providing an `OnUserInputRequest` handler: diff --git a/nodejs/README.md b/nodejs/README.md index af37b27bf..2e3f419a6 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -26,15 +26,16 @@ npm start ## Quick Start ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; // Create and start client const client = new CopilotClient(); await client.start(); -// Create a session +// Create a session (onPermissionRequest is required) const session = await client.createSession({ model: "gpt-5", + onPermissionRequest: approveAll, }); // Wait for response using typed event handlers @@ -59,7 +60,7 @@ await client.stop(); Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+/Node.js 18.0+): ```typescript -await using session = await client.createSession({ model: "gpt-5" }); +await using session = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll }); // session is automatically disconnected when leaving scope ``` @@ -114,6 +115,7 @@ Create a new conversation session. - `systemMessage?: SystemMessageConfig` - System message customization (see below) - `infiniteSessions?: InfiniteSessionConfig` - Configure automatic context compaction (see below) - `provider?: ProviderConfig` - Custom API provider configuration (BYOK - Bring Your Own Key). See [Custom Providers](#custom-providers) section. +- `onPermissionRequest: PermissionHandler` - **Required.** Handler called before each tool execution to approve or deny it. Use `approveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. - `onUserInputRequest?: UserInputHandler` - Handler for user input requests from the agent. Enables the `ask_user` tool. See [User Input Requests](#user-input-requests) section. - `hooks?: SessionHooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. @@ -648,6 +650,79 @@ const client = new CopilotClient({ Inbound trace context from the CLI is available on the `ToolInvocation` object passed to tool handlers as `traceparent` and `tracestate` fields. See the [OpenTelemetry guide](../docs/observability/opentelemetry.md) for a full wire-up example. +## Permission Handling + +An `onPermissionRequest` handler is **required** whenever you create or resume a session. The handler is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and must return a decision. + +### Approve All (simplest) + +Use the built-in `approveAll` helper to allow every tool call without any checks: + +```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const session = await client.createSession({ + model: "gpt-5", + onPermissionRequest: approveAll, +}); +``` + +### Custom Permission Handler + +Provide your own function to inspect each request and apply custom logic: + +```typescript +import type { PermissionRequest, PermissionRequestResult } from "@github/copilot-sdk"; + +const session = await client.createSession({ + model: "gpt-5", + onPermissionRequest: (request: PermissionRequest, invocation): PermissionRequestResult => { + // request.kind — what type of operation is being requested: + // "shell" — executing a shell command + // "write" — writing or editing a file + // "read" — reading a file + // "mcp" — calling an MCP tool + // "custom-tool" — calling one of your registered tools + // "url" — fetching a URL + // request.toolCallId — the tool call that triggered this request + // request.toolName — name of the tool (for custom-tool / mcp) + // request.fileName — file being written (for write) + // request.fullCommandText — full shell command (for shell) + + if (request.kind === "shell") { + // Deny shell commands + return { kind: "denied-interactively-by-user" }; + } + + return { kind: "approved" }; + }, +}); +``` + +### Permission Result Kinds + +| Kind | Meaning | +|------|---------| +| `"approved"` | Allow the tool to run | +| `"denied-interactively-by-user"` | User explicitly denied the request | +| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked | +| `"denied-by-rules"` | Denied by a policy rule | +| `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy | + +### Resuming Sessions + +Pass `onPermissionRequest` when resuming a session too — it is required: + +```typescript +const session = await client.resumeSession("session-id", { + onPermissionRequest: approveAll, +}); +``` + +### Per-Tool Skip Permission + +To let a specific custom tool bypass the permission prompt entirely, set `skipPermission: true` on the tool definition. See [Skipping Permission Prompts](#skipping-permission-prompts) under Tools. + ## User Input Requests Enable the agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler: diff --git a/python/README.md b/python/README.md index 6d1c81281..f2af2ad8d 100644 --- a/python/README.md +++ b/python/README.md @@ -25,15 +25,18 @@ python chat.py ```python import asyncio -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): # Create and start client client = CopilotClient() await client.start() - # Create a session - session = await client.create_session({"model": "gpt-5"}) + # Create a session (on_permission_request is required) + session = await client.create_session({ + "model": "gpt-5", + "on_permission_request": PermissionHandler.approve_all, + }) # Wait for response using session.idle event done = asyncio.Event() @@ -60,7 +63,10 @@ asyncio.run(main()) Sessions also support the `async with` context manager pattern for automatic cleanup: ```python -async with await client.create_session({"model": "gpt-5"}) as session: +async with await client.create_session({ + "model": "gpt-5", + "on_permission_request": PermissionHandler.approve_all, +}) as session: await session.send("What is 2+2?") # session is automatically disconnected when leaving the block ``` @@ -144,6 +150,7 @@ CopilotClient( - `streaming` (bool): Enable streaming delta events - `provider` (dict): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. - `infinite_sessions` (dict): Automatic context compaction configuration +- `on_permission_request` (callable): **Required.** Handler called before each tool execution to approve or deny it. Use `PermissionHandler.approve_all` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section. - `on_user_input_request` (callable): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `hooks` (dict): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. @@ -469,6 +476,88 @@ Trace context (`traceparent`/`tracestate`) is automatically propagated between t Install with telemetry extras: `pip install copilot-sdk[telemetry]` (provides `opentelemetry-api`) +## Permission Handling + +An `on_permission_request` handler is **required** whenever you create or resume a session. The handler is called before the agent executes each tool (file writes, shell commands, custom tools, etc.) and must return a decision. + +### Approve All (simplest) + +Use the built-in `PermissionHandler.approve_all` helper to allow every tool call without any checks: + +```python +from copilot import CopilotClient, PermissionHandler + +session = await client.create_session({ + "model": "gpt-5", + "on_permission_request": PermissionHandler.approve_all, +}) +``` + +### Custom Permission Handler + +Provide your own function to inspect each request and apply custom logic (sync or async): + +```python +from copilot import PermissionRequest, PermissionRequestResult + +def on_permission_request(request: PermissionRequest, invocation: dict) -> PermissionRequestResult: + # request.kind — what type of operation is being requested: + # "shell" — executing a shell command + # "write" — writing or editing a file + # "read" — reading a file + # "mcp" — calling an MCP tool + # "custom-tool" — calling one of your registered tools + # "url" — fetching a URL + # request.tool_call_id — the tool call that triggered this request + # request.tool_name — name of the tool (for custom-tool / mcp) + # request.file_name — file being written (for write) + # request.full_command_text — full shell command (for shell) + + if request.kind.value == "shell": + # Deny shell commands + return PermissionRequestResult(kind="denied-interactively-by-user") + + return PermissionRequestResult(kind="approved") + +session = await client.create_session({ + "model": "gpt-5", + "on_permission_request": on_permission_request, +}) +``` + +Async handlers are also supported: + +```python +async def on_permission_request(request: PermissionRequest, invocation: dict) -> PermissionRequestResult: + # Simulate an async approval check (e.g., prompting a user over a network) + await asyncio.sleep(0) + return PermissionRequestResult(kind="approved") +``` + +### Permission Result Kinds + +| `kind` value | Meaning | +|---|---------| +| `"approved"` | Allow the tool to run | +| `"denied-interactively-by-user"` | User explicitly denied the request | +| `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked (default when no kind is specified) | +| `"denied-by-rules"` | Denied by a policy rule | +| `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy | + +### Resuming Sessions + +Pass `on_permission_request` when resuming a session too — it is required: + +```python +session = await client.resume_session("session-id", { + "on_permission_request": PermissionHandler.approve_all, +}) +``` + +### Per-Tool Skip Permission + +To let a specific custom tool bypass the permission prompt entirely, set `skip_permission=True` on the tool definition. See [Skipping Permission Prompts](#skipping-permission-prompts) under Tools. + ## User Input Requests Enable the agent to ask questions to the user using the `ask_user` tool by providing an `on_user_input_request` handler: From bbd5acb67a6a57fbbee4d6f032d30dea199c5b46 Mon Sep 17 00:00:00 2001 From: James Montemagno Date: Tue, 17 Mar 2026 15:41:43 -0700 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- dotnet/README.md | 26 ++++++++++++++------------ go/README.md | 4 +++- nodejs/README.md | 5 ++++- python/README.md | 3 +++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/dotnet/README.md b/dotnet/README.md index 817bb4f8c..482de00d8 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -607,19 +607,21 @@ var session = await client.CreateSessionAsync(new SessionConfig Model = "gpt-5", OnPermissionRequest = async (request, invocation) => { - // request.Kind — what type of operation is being requested: - // PermissionRequestKind.Shell — executing a shell command - // PermissionRequestKind.Write — writing or editing a file - // PermissionRequestKind.Read — reading a file - // PermissionRequestKind.Mcp — calling an MCP tool - // PermissionRequestKind.CustomTool — calling one of your registered tools - // PermissionRequestKind.Url — fetching a URL - // request.ToolCallId — the tool call that triggered this request - // request.ToolName — name of the tool (for custom-tool / mcp) - // request.FileName — file being written (for write) + // request.Kind — string discriminator for the type of operation being requested: + // "shell" — executing a shell command + // "write" — writing or editing a file + // "read" — reading a file + // "mcp" — calling an MCP tool + // "custom_tool" — calling one of your registered tools + // "url" — fetching a URL + // "memory" — accessing or modifying assistant memory + // "hook" — invoking a registered hook + // request.ToolCallId — the tool call that triggered this request + // request.ToolName — name of the tool (for custom-tool / mcp) + // request.FileName — file being written (for write) // request.FullCommandText — full shell command text (for shell) - if (request.Kind == PermissionRequestKind.Shell) + if (request.Kind == "shell") { // Deny shell commands return new PermissionRequestResult { Kind = PermissionRequestResultKind.DeniedInteractivelyByUser }; @@ -638,7 +640,7 @@ var session = await client.CreateSessionAsync(new SessionConfig | `PermissionRequestResultKind.DeniedInteractivelyByUser` | User explicitly denied the request | | `PermissionRequestResultKind.DeniedCouldNotRequestFromUser` | No approval rule matched and user could not be asked | | `PermissionRequestResultKind.DeniedByRules` | Denied by a policy rule | -| `PermissionRequestResultKind.NoResult` | No decision (treated as denied) | +| `PermissionRequestResultKind.NoResult` | Leave the permission request unanswered (the SDK returns without calling the RPC). Not allowed for protocol v2 permission requests (will be rejected). | ### Resuming Sessions diff --git a/go/README.md b/go/README.md index 3ad9006fd..f22666f73 100644 --- a/go/README.md +++ b/go/README.md @@ -532,6 +532,8 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi // copilot.MCP — calling an MCP tool // copilot.CustomTool — calling one of your registered tools // copilot.URL — fetching a URL + // copilot.Memory — accessing or updating Copilot-managed memory + // copilot.Hook — invoking a registered hook // request.ToolCallID — pointer to the tool call that triggered this request // request.ToolName — pointer to the name of the tool (for custom-tool / mcp) // request.FileName — pointer to the file being written (for write) @@ -555,7 +557,7 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi | `PermissionRequestResultKindDeniedInteractivelyByUser` | User explicitly denied the request | | `PermissionRequestResultKindDeniedCouldNotRequestFromUser` | No approval rule matched and user could not be asked | | `PermissionRequestResultKindDeniedByRules` | Denied by a policy rule | -| `PermissionRequestResultKindNoResult` | No decision (treated as denied) | +| `PermissionRequestResultKindNoResult` | Leave the permission request unanswered (protocol v1 only; not allowed for protocol v2) | ### Resuming Sessions diff --git a/nodejs/README.md b/nodejs/README.md index 2e3f419a6..75cc33d46 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -684,6 +684,9 @@ const session = await client.createSession({ // "mcp" — calling an MCP tool // "custom-tool" — calling one of your registered tools // "url" — fetching a URL + // "memory" — storing or retrieving persistent session memory + // "hook" — invoking a server-side hook or integration + // (additional kinds may be added; include a default case in handlers) // request.toolCallId — the tool call that triggered this request // request.toolName — name of the tool (for custom-tool / mcp) // request.fileName — file being written (for write) @@ -708,7 +711,7 @@ const session = await client.createSession({ | `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked | | `"denied-by-rules"` | Denied by a policy rule | | `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy | - +| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | ### Resuming Sessions Pass `onPermissionRequest` when resuming a session too — it is required: diff --git a/python/README.md b/python/README.md index f2af2ad8d..5d08e7fcb 100644 --- a/python/README.md +++ b/python/README.md @@ -508,6 +508,8 @@ def on_permission_request(request: PermissionRequest, invocation: dict) -> Permi # "mcp" — calling an MCP tool # "custom-tool" — calling one of your registered tools # "url" — fetching a URL + # "memory" — accessing or updating session/workspace memory + # "hook" — invoking a registered hook # request.tool_call_id — the tool call that triggered this request # request.tool_name — name of the tool (for custom-tool / mcp) # request.file_name — file being written (for write) @@ -543,6 +545,7 @@ async def on_permission_request(request: PermissionRequest, invocation: dict) -> | `"denied-no-approval-rule-and-could-not-request-from-user"` | No approval rule matched and user could not be asked (default when no kind is specified) | | `"denied-by-rules"` | Denied by a policy rule | | `"denied-by-content-exclusion-policy"` | Denied due to a content exclusion policy | +| `"no-result"` | Leave the request unanswered (not allowed for protocol v2 permission requests) | ### Resuming Sessions