feat: introduce Reject variant of AskForApproval

This commit is contained in:
Michael Bolin
2026-02-17 21:24:17 -08:00
parent 31cbebd3c2
commit 5c0c0b9e8f
33 changed files with 1196 additions and 96 deletions
@@ -69,13 +69,46 @@
"type": "object"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"AskForApproval2": {
"description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
@@ -101,6 +134,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval2",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -117,6 +117,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -454,6 +454,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -221,6 +221,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -10667,13 +10700,46 @@
"type": "object"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"AuthMode": {
"description": "Authentication mode for OpenAI-backed providers.",
@@ -25,6 +25,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -117,6 +117,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -29,6 +29,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -25,6 +25,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -25,6 +25,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -117,6 +117,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -29,6 +29,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -117,6 +117,39 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -47,13 +47,46 @@
"type": "object"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"Config": {
"additionalProperties": true,
@@ -2,13 +2,46 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ConfigRequirements": {
"properties": {
@@ -2,13 +2,46 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"SandboxMode": {
"enum": [
@@ -6,13 +6,46 @@
"type": "string"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ByteRange": {
"properties": {
@@ -2,13 +2,46 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ContentItem": {
"oneOf": [
@@ -6,13 +6,46 @@
"type": "string"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ByteRange": {
"properties": {
@@ -2,13 +2,46 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"DynamicToolSpec": {
"properties": {
@@ -6,13 +6,46 @@
"type": "string"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ByteRange": {
"properties": {
@@ -6,13 +6,46 @@
"type": "string"
},
"AskForApproval": {
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"title": "RejectAskForApproval",
"type": "object"
}
]
},
"ByteRange": {
"properties": {
@@ -6,4 +6,16 @@
* Determines the conditions under which the user is consulted to approve
* running the command proposed by Codex.
*/
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | "never";
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | { "reject": {
/**
* Reject approval prompts related to sandbox escalation.
*/
sandbox_approval: boolean,
/**
* Reject prompts triggered by execpolicy `prompt` rules.
*/
rules: boolean,
/**
* Reject MCP elicitation prompts.
*/
mcp_elicitations: boolean, } } | "never";
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | "never";
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | { "reject": { sandbox_approval: boolean, rules: boolean, mcp_elicitations: boolean, } } | "never";
@@ -161,6 +161,11 @@ pub enum AskForApproval {
UnlessTrusted,
OnFailure,
OnRequest,
Reject {
sandbox_approval: bool,
rules: bool,
mcp_elicitations: bool,
},
Never,
}
@@ -170,6 +175,15 @@ impl AskForApproval {
AskForApproval::UnlessTrusted => CoreAskForApproval::UnlessTrusted,
AskForApproval::OnFailure => CoreAskForApproval::OnFailure,
AskForApproval::OnRequest => CoreAskForApproval::OnRequest,
AskForApproval::Reject {
sandbox_approval,
rules,
mcp_elicitations,
} => CoreAskForApproval::Reject {
sandbox_approval,
rules,
mcp_elicitations,
},
AskForApproval::Never => CoreAskForApproval::Never,
}
}
@@ -181,6 +195,15 @@ impl From<CoreAskForApproval> for AskForApproval {
CoreAskForApproval::UnlessTrusted => AskForApproval::UnlessTrusted,
CoreAskForApproval::OnFailure => AskForApproval::OnFailure,
CoreAskForApproval::OnRequest => AskForApproval::OnRequest,
CoreAskForApproval::Reject {
sandbox_approval,
rules,
mcp_elicitations,
} => AskForApproval::Reject {
sandbox_approval,
rules,
mcp_elicitations,
},
CoreAskForApproval::Never => AskForApproval::Never,
}
}
+72 -1
View File
@@ -133,6 +133,77 @@
],
"type": "string"
},
{
"additionalProperties": false,
"description": "Fine-grained rejection controls for approval prompts.\n\nWhen a field is `true`, prompts of that category are automatically rejected instead of shown to the user.",
"properties": {
"reject": {
"properties": {
"mcp_elicitations": {
"description": "Reject MCP elicitation prompts.",
"type": "boolean"
},
"rules": {
"description": "Reject prompts triggered by execpolicy `prompt` rules.",
"type": "boolean"
},
"sandbox_approval": {
"description": "Reject approval prompts related to sandbox escalation.",
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"reject"
],
"type": "object"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
"never"
],
"type": "string"
}
]
},
"AskForApprovalToml": {
"oneOf": [
{
"description": "Under this policy, only \"known safe\" commands—those matching `is_safe_command()` and performing read-only file operations—are auto-approved. Everything else will ask the user to approve.",
"enum": [
"untrusted"
],
"type": "string"
},
{
"description": "DEPRECATED: *All* commands are auto-approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox. Prefer `OnRequest` for interactive runs or `Never` for non-interactive runs.",
"enum": [
"on-failure"
],
"type": "string"
},
{
"description": "The model decides when to ask the user for approval.",
"enum": [
"on-request"
],
"type": "string"
},
{
"description": "Reject sandbox escalation approvals by default, while still allowing rule-driven prompts and MCP elicitations.",
"enum": [
"reject"
],
"type": "string"
},
{
"description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.",
"enum": [
@@ -1280,7 +1351,7 @@
"approval_policy": {
"allOf": [
{
"$ref": "#/definitions/AskForApproval"
"$ref": "#/definitions/AskForApprovalToml"
}
],
"description": "Default approval policy for executing commands."
+9 -4
View File
@@ -1380,10 +1380,9 @@ impl Session {
required_mcp_servers.sort();
let cancel_token = sess.mcp_startup_cancellation_token().await;
sess.services
.mcp_connection_manager
.write()
.await
let mut mcp_connection_manager = sess.services.mcp_connection_manager.write().await;
mcp_connection_manager.set_approval_policy(session_configuration.approval_policy.value());
mcp_connection_manager
.initialize(
&mcp_servers,
config.mcp_oauth_credentials_store_mode,
@@ -1393,6 +1392,7 @@ impl Session {
sandbox_state,
)
.await;
drop(mcp_connection_manager);
if !required_mcp_servers.is_empty() {
let failures = sess
.services
@@ -1828,6 +1828,11 @@ impl Session {
sandbox_policy_changed: bool,
) -> Arc<TurnContext> {
let per_turn_config = Self::build_per_turn_config(&session_configuration);
self.services
.mcp_connection_manager
.read()
.await
.set_approval_policy(session_configuration.approval_policy.value());
if sandbox_policy_changed {
let sandbox_state = SandboxState {
+62 -3
View File
@@ -893,7 +893,7 @@ pub struct ConfigToml {
pub model_auto_compact_token_limit: Option<i64>,
/// Default approval policy for executing commands.
pub approval_policy: Option<AskForApproval>,
pub approval_policy: Option<AskForApprovalToml>,
#[serde(default)]
pub shell_environment_policy: ShellEnvironmentPolicyToml,
@@ -1113,7 +1113,7 @@ impl From<ConfigToml> for UserSavedConfig {
.collect();
Self {
approval_policy: config_toml.approval_policy,
approval_policy: config_toml.approval_policy.map(Into::into),
sandbox_mode: config_toml.sandbox_mode,
sandbox_settings: config_toml.sandbox_workspace_write.map(From::from),
forced_chatgpt_workspace_id: config_toml.forced_chatgpt_workspace_id,
@@ -1129,6 +1129,65 @@ impl From<ConfigToml> for UserSavedConfig {
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum AskForApprovalToml {
/// Under this policy, only "known safe" commands—those matching
/// `is_safe_command()` and performing read-only file operations—are
/// auto-approved. Everything else will ask the user to approve.
#[serde(rename = "untrusted")]
UnlessTrusted,
/// DEPRECATED: *All* commands are auto-approved, but they are expected to
/// run inside a sandbox where network access is disabled and writes are
/// confined to a specific set of paths. If the command fails, it will be
/// escalated to the user to approve execution without a sandbox.
/// Prefer `OnRequest` for interactive runs or `Never` for non-interactive
/// runs.
OnFailure,
/// The model decides when to ask the user for approval.
#[default]
OnRequest,
/// Reject sandbox escalation approvals by default, while still allowing
/// rule-driven prompts and MCP elicitations.
Reject,
/// Never ask the user to approve commands. Failures are immediately returned
/// to the model, and never escalated to the user for approval.
Never,
}
impl AskForApprovalToml {
pub fn to_core(self) -> AskForApproval {
match self {
AskForApprovalToml::UnlessTrusted => AskForApproval::UnlessTrusted,
AskForApprovalToml::OnFailure => AskForApproval::OnFailure,
AskForApprovalToml::OnRequest => AskForApproval::OnRequest,
AskForApprovalToml::Reject => AskForApproval::reject_defaults(),
AskForApprovalToml::Never => AskForApproval::Never,
}
}
}
impl From<AskForApproval> for AskForApprovalToml {
fn from(value: AskForApproval) -> Self {
match value {
AskForApproval::UnlessTrusted => AskForApprovalToml::UnlessTrusted,
AskForApproval::OnFailure => AskForApprovalToml::OnFailure,
AskForApproval::OnRequest => AskForApprovalToml::OnRequest,
AskForApproval::Reject { .. } => AskForApprovalToml::Reject,
AskForApproval::Never => AskForApprovalToml::Never,
}
}
}
impl From<AskForApprovalToml> for AskForApproval {
fn from(value: AskForApprovalToml) -> Self {
value.to_core()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema)]
#[schemars(deny_unknown_fields)]
pub struct ProjectConfig {
@@ -1571,7 +1630,7 @@ impl Config {
|| cfg.approval_policy.is_some();
let mut approval_policy = approval_policy_override
.or(config_profile.approval_policy)
.or(cfg.approval_policy)
.or(cfg.approval_policy.map(Into::into))
.unwrap_or_else(|| {
if active_project.is_trusted() {
AskForApproval::OnRequest
+46 -2
View File
@@ -32,6 +32,10 @@ use shlex::try_join as shlex_try_join;
const PROMPT_CONFLICT_REASON: &str =
"approval required by policy, but AskForApproval is set to Never";
const REJECT_SANDBOX_APPROVAL_REASON: &str =
"approval required by policy, but AskForApproval::Reject.sandbox_approval is set";
const REJECT_RULES_APPROVAL_REASON: &str =
"approval required by policy rule, but AskForApproval::Reject.rules is set";
const RULES_DIR_NAME: &str = "rules";
const RULE_EXTENSION: &str = "rules";
const DEFAULT_POLICY_FILE: &str = "default.rules";
@@ -91,6 +95,30 @@ fn is_policy_match(rule_match: &RuleMatch) -> bool {
}
}
fn prompt_is_from_policy_rule(evaluation: &Evaluation) -> bool {
evaluation
.matched_rules
.iter()
.any(|rule_match| is_policy_match(rule_match) && rule_match.decision() == Decision::Prompt)
}
fn prompt_is_rejected_by_policy(
approval_policy: AskForApproval,
prompt_is_rule: bool,
) -> Option<&'static str> {
match approval_policy {
AskForApproval::Never => Some(PROMPT_CONFLICT_REASON),
AskForApproval::Reject { rules: true, .. } if prompt_is_rule => {
Some(REJECT_RULES_APPROVAL_REASON)
}
AskForApproval::Reject {
sandbox_approval: true,
..
} if !prompt_is_rule => Some(REJECT_SANDBOX_APPROVAL_REASON),
_ => None,
}
}
#[derive(Debug, Error)]
pub enum ExecPolicyError {
#[error("failed to read rules files from {dir}: {source}")]
@@ -196,9 +224,11 @@ impl ExecPolicyManager {
reason: derive_forbidden_reason(command, &evaluation),
},
Decision::Prompt => {
if matches!(approval_policy, AskForApproval::Never) {
let prompt_is_rule = prompt_is_from_policy_rule(&evaluation);
if let Some(reason) = prompt_is_rejected_by_policy(approval_policy, prompt_is_rule)
{
ExecApprovalRequirement::Forbidden {
reason: PROMPT_CONFLICT_REASON.to_string(),
reason: reason.to_string(),
}
} else {
ExecApprovalRequirement::NeedsApproval {
@@ -465,6 +495,20 @@ pub fn render_decision_for_unmatched_command(
}
}
}
AskForApproval::Reject { .. } => match sandbox_policy {
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
// Mirror on-request behavior for unmatched commands; prompt-vs-reject is handled
// by `prompt_is_rejected_by_policy`.
Decision::Allow
}
SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. } => {
if sandbox_permissions.requires_escalated_permissions() {
Decision::Prompt
} else {
Decision::Allow
}
}
},
}
}
+39 -1
View File
@@ -28,6 +28,7 @@ use codex_async_utils::OrCancelExt;
use codex_protocol::approvals::ElicitationRequestEvent;
use codex_protocol::mcp::CallToolResult;
use codex_protocol::mcp::RequestId as ProtocolRequestId;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::McpStartupCompleteEvent;
@@ -44,6 +45,7 @@ use futures::future::FutureExt;
use futures::future::Shared;
use rmcp::model::ClientCapabilities;
use rmcp::model::CreateElicitationRequestParams;
use rmcp::model::ElicitationAction;
use rmcp::model::ElicitationCapability;
use rmcp::model::FormElicitationCapability;
use rmcp::model::Implementation;
@@ -182,9 +184,19 @@ static CODEX_APPS_TOOLS_CACHE: LazyLock<StdMutex<Option<CachedCodexAppsTools>>>
type ResponderMap = HashMap<(String, RequestId), oneshot::Sender<ElicitationResponse>>;
#[derive(Clone, Default)]
#[derive(Clone)]
struct ElicitationRequestManager {
requests: Arc<Mutex<ResponderMap>>,
approval_policy: Arc<StdMutex<AskForApproval>>,
}
impl Default for ElicitationRequestManager {
fn default() -> Self {
Self {
requests: Arc::new(Mutex::new(HashMap::new())),
approval_policy: Arc::new(StdMutex::new(AskForApproval::default())),
}
}
}
impl ElicitationRequestManager {
@@ -205,11 +217,23 @@ impl ElicitationRequestManager {
fn make_sender(&self, server_name: String, tx_event: Sender<Event>) -> SendElicitation {
let elicitation_requests = self.requests.clone();
let approval_policy = self.approval_policy.clone();
Box::new(move |id, elicitation| {
let elicitation_requests = elicitation_requests.clone();
let tx_event = tx_event.clone();
let server_name = server_name.clone();
let approval_policy = approval_policy.clone();
async move {
if approval_policy
.lock()
.is_ok_and(|policy| policy.rejects_mcp_elicitations())
{
return Ok(ElicitationResponse {
action: ElicitationAction::Decline,
content: None,
});
}
let (tx, rx) = oneshot::channel();
{
let mut lock = elicitation_requests.lock().await;
@@ -357,6 +381,12 @@ impl McpConnectionManager {
!self.clients.is_empty()
}
pub fn set_approval_policy(&self, approval_policy: AskForApproval) {
if let Ok(mut policy) = self.elicitation_requests.approval_policy.lock() {
*policy = approval_policy;
}
}
pub async fn initialize(
&mut self,
mcp_servers: &HashMap<String, McpServerConfig>,
@@ -371,7 +401,15 @@ impl McpConnectionManager {
}
let mut clients = HashMap::new();
let mut join_set = JoinSet::new();
let approval_policy = self
.elicitation_requests
.approval_policy
.lock()
.map_or_else(|_| AskForApproval::default(), |policy| *policy);
let elicitation_requests = ElicitationRequestManager::default();
if let Ok(mut policy) = elicitation_requests.approval_policy.lock() {
*policy = approval_policy;
}
let mcp_servers = mcp_servers.clone();
for (server_name, cfg) in mcp_servers.into_iter().filter(|(_, cfg)| cfg.enabled) {
let cancel_token = cancel_token.child_token();
+16 -3
View File
@@ -38,7 +38,10 @@ pub fn assess_patch_safety(
}
match policy {
AskForApproval::OnFailure | AskForApproval::Never | AskForApproval::OnRequest => {
AskForApproval::OnFailure
| AskForApproval::Never
| AskForApproval::OnRequest
| AskForApproval::Reject { .. } => {
// Continue to see if this can be auto-approved.
}
// TODO(ragona): I'm not sure this is actually correct? I believe in this case
@@ -52,7 +55,10 @@ pub fn assess_patch_safety(
// possible that paths in the patch are hard links to files outside the
// writable roots, so we should still run `apply_patch` in a sandbox in that case.
if is_write_patch_constrained_to_writable_paths(action, sandbox_policy, cwd)
|| policy == AskForApproval::OnFailure
|| matches!(
policy,
AskForApproval::OnFailure | AskForApproval::Reject { .. }
)
{
if matches!(
sandbox_policy,
@@ -75,7 +81,14 @@ pub fn assess_patch_safety(
None => SafetyCheck::AskUser,
}
}
} else if policy == AskForApproval::Never {
} else if matches!(
policy,
AskForApproval::Never
| AskForApproval::Reject {
sandbox_approval: true,
..
}
) {
SafetyCheck::Reject {
reason: "writing outside of the project; rejected by user approval settings"
.to_string(),
+17 -3
View File
@@ -156,6 +156,8 @@ impl ExecApprovalRequirement {
/// - Never, OnFailure: do not ask
/// - OnRequest: ask unless sandbox policy is DangerFullAccess
/// - Reject: ask unless sandbox policy is DangerFullAccess, but auto-reject
/// when `sandbox_approval` is enabled.
/// - UnlessTrusted: always ask
pub(crate) fn default_exec_approval_requirement(
policy: AskForApproval,
@@ -163,14 +165,18 @@ pub(crate) fn default_exec_approval_requirement(
) -> ExecApprovalRequirement {
let needs_approval = match policy {
AskForApproval::Never | AskForApproval::OnFailure => false,
AskForApproval::OnRequest => !matches!(
AskForApproval::OnRequest | AskForApproval::Reject { .. } => !matches!(
sandbox_policy,
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
),
AskForApproval::UnlessTrusted => true,
};
if needs_approval {
if needs_approval && policy.rejects_sandbox_approval() {
ExecApprovalRequirement::Forbidden {
reason: "approval policy rejected sandbox approval prompt".to_string(),
}
} else if needs_approval {
ExecApprovalRequirement::NeedsApproval {
reason: None,
proposed_execpolicy_amendment: None,
@@ -224,7 +230,15 @@ pub(crate) trait Approvable<Req> {
/// Decide we can request an approval for no-sandbox execution.
fn wants_no_sandbox_approval(&self, policy: AskForApproval) -> bool {
!matches!(policy, AskForApproval::Never | AskForApproval::OnRequest)
match policy {
AskForApproval::OnFailure => true,
AskForApproval::UnlessTrusted => true,
AskForApproval::Never => false,
AskForApproval::OnRequest => false,
AskForApproval::Reject {
sandbox_approval, ..
} => !sandbox_approval,
}
}
fn start_approval_async<'a>(
+13
View File
@@ -255,6 +255,19 @@ impl DeveloperInstructions {
None => APPROVAL_POLICY_ON_REQUEST_RULE.to_string(),
}
}
AskForApproval::Reject {
sandbox_approval,
rules,
mcp_elicitations,
} => {
format!(
"Approval policy is `reject`.\n\
- `sandbox_approval`: {sandbox_approval}\n\
- `rules`: {rules}\n\
- `mcp_elicitations`: {mcp_elicitations}\n\
When a category is `true`, requests in that category are auto-rejected instead of prompting the user."
)
}
};
DeveloperInstructions::new(text)
+49
View File
@@ -377,11 +377,60 @@ pub enum AskForApproval {
#[default]
OnRequest,
/// Fine-grained rejection controls for approval prompts.
///
/// When a field is `true`, prompts of that category are automatically
/// rejected instead of shown to the user.
Reject {
/// Reject approval prompts related to sandbox escalation.
sandbox_approval: bool,
/// Reject prompts triggered by execpolicy `prompt` rules.
rules: bool,
/// Reject MCP elicitation prompts.
mcp_elicitations: bool,
},
/// Never ask the user to approve commands. Failures are immediately returned
/// to the model, and never escalated to the user for approval.
Never,
}
impl AskForApproval {
/// Backward-compatible defaults for `approval_policy = "reject"` in
/// `config.toml`.
pub const fn reject_defaults() -> Self {
Self::Reject {
sandbox_approval: true,
rules: false,
mcp_elicitations: false,
}
}
pub const fn rejects_sandbox_approval(self) -> bool {
matches!(
self,
Self::Reject {
sandbox_approval: true,
..
}
)
}
pub const fn rejects_rules_approval(self) -> bool {
matches!(self, Self::Reject { rules: true, .. })
}
pub const fn rejects_mcp_elicitations(self) -> bool {
matches!(
self,
Self::Reject {
mcp_elicitations: true,
..
}
)
}
}
/// Represents whether outbound network access is available to the agent.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, Default, JsonSchema, TS,