mirror of
https://github.com/openai/codex.git
synced 2026-11-14 21:40:39 +00:00
feat: introduce Reject variant of AskForApproval
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -36,6 +36,7 @@ use codex_protocol::protocol::PatchApplyStatus as CorePatchApplyStatus;
|
||||
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
|
||||
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
|
||||
use codex_protocol::protocol::ReadOnlyAccess as CoreReadOnlyAccess;
|
||||
use codex_protocol::protocol::RejectConfig as CoreRejectConfig;
|
||||
use codex_protocol::protocol::SessionSource as CoreSessionSource;
|
||||
use codex_protocol::protocol::SkillDependencies as CoreSkillDependencies;
|
||||
use codex_protocol::protocol::SkillErrorInfo as CoreSkillErrorInfo;
|
||||
@@ -161,6 +162,11 @@ pub enum AskForApproval {
|
||||
UnlessTrusted,
|
||||
OnFailure,
|
||||
OnRequest,
|
||||
Reject {
|
||||
sandbox_approval: bool,
|
||||
rules: bool,
|
||||
mcp_elicitations: bool,
|
||||
},
|
||||
Never,
|
||||
}
|
||||
|
||||
@@ -170,6 +176,15 @@ impl AskForApproval {
|
||||
AskForApproval::UnlessTrusted => CoreAskForApproval::UnlessTrusted,
|
||||
AskForApproval::OnFailure => CoreAskForApproval::OnFailure,
|
||||
AskForApproval::OnRequest => CoreAskForApproval::OnRequest,
|
||||
AskForApproval::Reject {
|
||||
sandbox_approval,
|
||||
rules,
|
||||
mcp_elicitations,
|
||||
} => CoreAskForApproval::Reject(CoreRejectConfig {
|
||||
sandbox_approval,
|
||||
rules,
|
||||
mcp_elicitations,
|
||||
}),
|
||||
AskForApproval::Never => CoreAskForApproval::Never,
|
||||
}
|
||||
}
|
||||
@@ -181,6 +196,11 @@ impl From<CoreAskForApproval> for AskForApproval {
|
||||
CoreAskForApproval::UnlessTrusted => AskForApproval::UnlessTrusted,
|
||||
CoreAskForApproval::OnFailure => AskForApproval::OnFailure,
|
||||
CoreAskForApproval::OnRequest => AskForApproval::OnRequest,
|
||||
CoreAskForApproval::Reject(reject_config) => AskForApproval::Reject {
|
||||
sandbox_approval: reject_config.sandbox_approval,
|
||||
rules: reject_config.rules,
|
||||
mcp_elicitations: reject_config.mcp_elicitations,
|
||||
},
|
||||
CoreAskForApproval::Never => AskForApproval::Never,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,38 +109,15 @@
|
||||
"description": "App/connector settings loaded from `config.toml`.",
|
||||
"type": "object"
|
||||
},
|
||||
"AskForApproval": {
|
||||
"description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** 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": "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": {
|
||||
"description": "Version of [AskForApproval] that is deserializable from config.toml.",
|
||||
"enum": [
|
||||
"untrusted",
|
||||
"on-failure",
|
||||
"on-request",
|
||||
"never"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AuthCredentialsStoreMode": {
|
||||
"description": "Determine where Codex should store CLI auth credentials.",
|
||||
@@ -183,7 +160,7 @@
|
||||
"$ref": "#/definitions/AnalyticsConfigToml"
|
||||
},
|
||||
"approval_policy": {
|
||||
"$ref": "#/definitions/AskForApproval"
|
||||
"$ref": "#/definitions/AskForApprovalToml"
|
||||
},
|
||||
"chatgpt_base_url": {
|
||||
"type": "string"
|
||||
@@ -1280,7 +1257,7 @@
|
||||
"approval_policy": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AskForApproval"
|
||||
"$ref": "#/definitions/AskForApprovalToml"
|
||||
}
|
||||
],
|
||||
"description": "Default approval policy for executing commands."
|
||||
|
||||
@@ -1263,7 +1263,9 @@ impl Session {
|
||||
};
|
||||
|
||||
let services = SessionServices {
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())),
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::new(
|
||||
&config.permissions.approval_policy,
|
||||
))),
|
||||
mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()),
|
||||
unified_exec_manager: UnifiedExecProcessManager::default(),
|
||||
analytics_events_client: AnalyticsEventsClient::new(
|
||||
@@ -1380,19 +1382,19 @@ 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
|
||||
.initialize(
|
||||
&mcp_servers,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
auth_statuses.clone(),
|
||||
&session_configuration.approval_policy,
|
||||
tx_event.clone(),
|
||||
cancel_token,
|
||||
sandbox_state,
|
||||
)
|
||||
.await;
|
||||
drop(mcp_connection_manager);
|
||||
if !required_mcp_servers.is_empty() {
|
||||
let failures = sess
|
||||
.services
|
||||
@@ -1828,6 +1830,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);
|
||||
|
||||
if sandbox_policy_changed {
|
||||
let sandbox_state = SandboxState {
|
||||
@@ -2997,12 +3004,14 @@ impl Session {
|
||||
};
|
||||
let cancel_token = self.reset_mcp_startup_cancellation_token().await;
|
||||
|
||||
let mut refreshed_manager = McpConnectionManager::default();
|
||||
let mut refreshed_manager =
|
||||
McpConnectionManager::new(&turn_context.config.permissions.approval_policy);
|
||||
refreshed_manager
|
||||
.initialize(
|
||||
&mcp_servers,
|
||||
store_mode,
|
||||
auth_statuses,
|
||||
&turn_context.config.permissions.approval_policy,
|
||||
self.get_tx_event(),
|
||||
cancel_token,
|
||||
sandbox_state,
|
||||
@@ -7265,7 +7274,9 @@ mod tests {
|
||||
|
||||
let file_watcher = Arc::new(FileWatcher::noop());
|
||||
let services = SessionServices {
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())),
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::new(
|
||||
&config.permissions.approval_policy,
|
||||
))),
|
||||
mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()),
|
||||
unified_exec_manager: UnifiedExecProcessManager::default(),
|
||||
analytics_events_client: AnalyticsEventsClient::new(
|
||||
@@ -7413,7 +7424,9 @@ mod tests {
|
||||
|
||||
let file_watcher = Arc::new(FileWatcher::noop());
|
||||
let services = SessionServices {
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())),
|
||||
mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::new(
|
||||
&config.permissions.approval_policy,
|
||||
))),
|
||||
mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()),
|
||||
unified_exec_manager: UnifiedExecProcessManager::default(),
|
||||
analytics_events_client: AnalyticsEventsClient::new(
|
||||
|
||||
@@ -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,34 @@ impl From<ConfigToml> for UserSavedConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Version of [AskForApproval] that is deserializable from config.toml.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum AskForApprovalToml {
|
||||
#[serde(rename = "untrusted")]
|
||||
UnlessTrusted,
|
||||
OnFailure,
|
||||
OnRequest,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl AskForApprovalToml {
|
||||
pub fn to_core(self) -> AskForApproval {
|
||||
match self {
|
||||
AskForApprovalToml::UnlessTrusted => AskForApproval::UnlessTrusted,
|
||||
AskForApprovalToml::OnFailure => AskForApproval::OnFailure,
|
||||
AskForApprovalToml::OnRequest => AskForApproval::OnRequest,
|
||||
AskForApprovalToml::Never => AskForApproval::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 {
|
||||
@@ -1570,8 +1598,8 @@ impl Config {
|
||||
|| config_profile.approval_policy.is_some()
|
||||
|| cfg.approval_policy.is_some();
|
||||
let mut approval_policy = approval_policy_override
|
||||
.or(config_profile.approval_policy)
|
||||
.or(cfg.approval_policy)
|
||||
.or(config_profile.approval_policy.map(Into::into))
|
||||
.or(cfg.approval_policy.map(Into::into))
|
||||
.unwrap_or_else(|| {
|
||||
if active_project.is_trusted() {
|
||||
AskForApproval::OnRequest
|
||||
|
||||
@@ -3,9 +3,9 @@ use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::AskForApprovalToml;
|
||||
use crate::config::types::Personality;
|
||||
use crate::config::types::WindowsToml;
|
||||
use crate::protocol::AskForApproval;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
@@ -21,7 +21,7 @@ pub struct ConfigProfile {
|
||||
/// The key in the `model_providers` map identifying the
|
||||
/// [`ModelProviderInfo`] to use.
|
||||
pub model_provider: Option<String>,
|
||||
pub approval_policy: Option<AskForApproval>,
|
||||
pub approval_policy: Option<AskForApprovalToml>,
|
||||
pub sandbox_mode: Option<SandboxMode>,
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
@@ -57,7 +57,7 @@ impl From<ConfigProfile> for codex_app_server_protocol::Profile {
|
||||
Self {
|
||||
model: config_profile.model,
|
||||
model_provider: config_profile.model_provider,
|
||||
approval_policy: config_profile.approval_policy,
|
||||
approval_policy: config_profile.approval_policy.map(Into::into),
|
||||
model_reasoning_effort: config_profile.model_reasoning_effort,
|
||||
model_reasoning_summary: config_profile.model_reasoning_summary,
|
||||
model_verbosity: config_profile.model_verbosity,
|
||||
|
||||
@@ -91,7 +91,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options(
|
||||
let auth_status_entries =
|
||||
compute_auth_statuses(mcp_servers.iter(), config.mcp_oauth_credentials_store_mode).await;
|
||||
|
||||
let mut mcp_connection_manager = McpConnectionManager::default();
|
||||
let mut mcp_connection_manager = McpConnectionManager::new(&config.permissions.approval_policy);
|
||||
let (tx_event, rx_event) = unbounded();
|
||||
drop(rx_event);
|
||||
let cancel_token = CancellationToken::new();
|
||||
@@ -108,6 +108,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options(
|
||||
&mcp_servers,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
auth_status_entries,
|
||||
&config.permissions.approval_policy,
|
||||
tx_event,
|
||||
cancel_token.clone(),
|
||||
sandbox_state,
|
||||
|
||||
@@ -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,32 @@ fn is_policy_match(rule_match: &RuleMatch) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a rejection reason when `approval_policy` disallows surfacing the
|
||||
/// current prompt to the user.
|
||||
///
|
||||
/// `prompt_is_rule` distinguishes policy-rule prompts from sandbox/escalation
|
||||
/// prompts so `Reject.rules` and `Reject.sandbox_approval` are honored
|
||||
/// independently.
|
||||
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(reject_config)
|
||||
if prompt_is_rule && reject_config.rejects_rules_approval() =>
|
||||
{
|
||||
Some(REJECT_RULES_APPROVAL_REASON)
|
||||
}
|
||||
AskForApproval::Reject(reject_config)
|
||||
if !prompt_is_rule && reject_config.rejects_sandbox_approval() =>
|
||||
{
|
||||
Some(REJECT_SANDBOX_APPROVAL_REASON)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecPolicyError {
|
||||
#[error("failed to read rules files from {dir}: {source}")]
|
||||
@@ -196,9 +226,13 @@ impl ExecPolicyManager {
|
||||
reason: derive_forbidden_reason(command, &evaluation),
|
||||
},
|
||||
Decision::Prompt => {
|
||||
if matches!(approval_policy, AskForApproval::Never) {
|
||||
let prompt_is_rule = evaluation.matched_rules.iter().any(|rule_match| {
|
||||
is_policy_match(rule_match) && rule_match.decision() == Decision::Prompt
|
||||
});
|
||||
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 +499,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
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,6 +1245,52 @@ prefix_rule(
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unmatched_reject_policy_still_prompts_for_restricted_sandbox_escalation() {
|
||||
let command = vec!["madeup-cmd".to_string()];
|
||||
|
||||
assert_eq!(
|
||||
Decision::Prompt,
|
||||
render_decision_for_unmatched_command(
|
||||
AskForApproval::Reject(RejectConfig {
|
||||
sandbox_approval: false,
|
||||
rules: false,
|
||||
mcp_elicitations: false,
|
||||
}),
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
&command,
|
||||
SandboxPermissions::RequireEscalated,
|
||||
false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn exec_approval_requirement_rejects_unmatched_prompt_when_sandbox_rejection_enabled() {
|
||||
let command = vec!["madeup-cmd".to_string()];
|
||||
|
||||
let requirement = ExecPolicyManager::default()
|
||||
.create_exec_approval_requirement_for_command(ExecApprovalRequest {
|
||||
command: &command,
|
||||
approval_policy: AskForApproval::Reject(RejectConfig {
|
||||
sandbox_approval: true,
|
||||
rules: false,
|
||||
mcp_elicitations: false,
|
||||
}),
|
||||
sandbox_policy: &SandboxPolicy::new_read_only_policy(),
|
||||
sandbox_permissions: SandboxPermissions::RequireEscalated,
|
||||
prefix_rule: None,
|
||||
})
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
requirement,
|
||||
ExecApprovalRequirement::Forbidden {
|
||||
reason: REJECT_SANDBOX_APPROVAL_REASON.to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn exec_approval_requirement_falls_back_to_heuristics() {
|
||||
let command = vec!["cargo".to_string(), "build".to_string()];
|
||||
|
||||
@@ -191,7 +191,7 @@ pub async fn collect_mcp_snapshot(config: &Config) -> McpListToolsResponseEvent
|
||||
let auth_status_entries =
|
||||
compute_auth_statuses(mcp_servers.iter(), config.mcp_oauth_credentials_store_mode).await;
|
||||
|
||||
let mut mcp_connection_manager = McpConnectionManager::default();
|
||||
let mut mcp_connection_manager = McpConnectionManager::new(&config.permissions.approval_policy);
|
||||
let (tx_event, rx_event) = unbounded();
|
||||
drop(rx_event);
|
||||
let cancel_token = CancellationToken::new();
|
||||
@@ -209,6 +209,7 @@ pub async fn collect_mcp_snapshot(config: &Config) -> McpListToolsResponseEvent
|
||||
&mcp_servers,
|
||||
config.mcp_oauth_credentials_store_mode,
|
||||
auth_status_entries.clone(),
|
||||
&config.permissions.approval_policy,
|
||||
tx_event,
|
||||
cancel_token.clone(),
|
||||
sandbox_state,
|
||||
|
||||
@@ -25,9 +25,11 @@ use anyhow::anyhow;
|
||||
use async_channel::Sender;
|
||||
use codex_async_utils::CancelErr;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_config::Constrained;
|
||||
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 +46,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,12 +185,20 @@ 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 ElicitationRequestManager {
|
||||
fn new(approval_policy: AskForApproval) -> Self {
|
||||
Self {
|
||||
requests: Arc::new(Mutex::new(HashMap::new())),
|
||||
approval_policy: Arc::new(StdMutex::new(approval_policy)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve(
|
||||
&self,
|
||||
server_name: String,
|
||||
@@ -205,11 +216,27 @@ 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| {
|
||||
matches!(*policy, AskForApproval::Never)
|
||||
|| matches!(
|
||||
*policy,
|
||||
AskForApproval::Reject(reject_config)
|
||||
if reject_config.rejects_mcp_elicitations()
|
||||
)
|
||||
}) {
|
||||
return Ok(ElicitationResponse {
|
||||
action: ElicitationAction::Decline,
|
||||
content: None,
|
||||
});
|
||||
}
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
{
|
||||
let mut lock = elicitation_requests.lock().await;
|
||||
@@ -346,22 +373,35 @@ pub struct SandboxState {
|
||||
}
|
||||
|
||||
/// A thin wrapper around a set of running [`RmcpClient`] instances.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct McpConnectionManager {
|
||||
clients: HashMap<String, AsyncManagedClient>,
|
||||
elicitation_requests: ElicitationRequestManager,
|
||||
}
|
||||
|
||||
impl McpConnectionManager {
|
||||
pub(crate) fn new(approval_policy: &Constrained<AskForApproval>) -> Self {
|
||||
Self {
|
||||
clients: HashMap::new(),
|
||||
elicitation_requests: ElicitationRequestManager::new(approval_policy.value()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_servers(&self) -> bool {
|
||||
!self.clients.is_empty()
|
||||
}
|
||||
|
||||
pub fn set_approval_policy(&self, approval_policy: &Constrained<AskForApproval>) {
|
||||
if let Ok(mut policy) = self.elicitation_requests.approval_policy.lock() {
|
||||
*policy = approval_policy.value();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn initialize(
|
||||
&mut self,
|
||||
mcp_servers: &HashMap<String, McpServerConfig>,
|
||||
store_mode: OAuthCredentialsStoreMode,
|
||||
auth_entries: HashMap<String, McpAuthStatusEntry>,
|
||||
approval_policy: &Constrained<AskForApproval>,
|
||||
tx_event: Sender<Event>,
|
||||
cancel_token: CancellationToken,
|
||||
initial_sandbox_state: SandboxState,
|
||||
@@ -371,7 +411,7 @@ impl McpConnectionManager {
|
||||
}
|
||||
let mut clients = HashMap::new();
|
||||
let mut join_set = JoinSet::new();
|
||||
let elicitation_requests = ElicitationRequestManager::default();
|
||||
let elicitation_requests = ElicitationRequestManager::new(approval_policy.value());
|
||||
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();
|
||||
|
||||
@@ -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
|
||||
@@ -48,11 +51,20 @@ pub fn assess_patch_safety(
|
||||
}
|
||||
}
|
||||
|
||||
let rejects_sandbox_approval = matches!(policy, AskForApproval::Never)
|
||||
|| matches!(
|
||||
policy,
|
||||
AskForApproval::Reject(reject_config) if reject_config.sandbox_approval
|
||||
);
|
||||
|
||||
// Even though the patch appears to be constrained to writable paths, it is
|
||||
// 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,
|
||||
@@ -72,10 +84,20 @@ pub fn assess_patch_safety(
|
||||
sandbox_type,
|
||||
user_explicitly_approved: false,
|
||||
},
|
||||
None => SafetyCheck::AskUser,
|
||||
None => {
|
||||
if rejects_sandbox_approval {
|
||||
SafetyCheck::Reject {
|
||||
reason:
|
||||
"writing outside of the project; rejected by user approval settings"
|
||||
.to_string(),
|
||||
}
|
||||
} else {
|
||||
SafetyCheck::AskUser
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if policy == AskForApproval::Never {
|
||||
} else if rejects_sandbox_approval {
|
||||
SafetyCheck::Reject {
|
||||
reason: "writing outside of the project; rejected by user approval settings"
|
||||
.to_string(),
|
||||
|
||||
@@ -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` rejection is enabled.
|
||||
/// - UnlessTrusted: always ask
|
||||
pub(crate) fn default_exec_approval_requirement(
|
||||
policy: AskForApproval,
|
||||
@@ -163,14 +165,23 @@ 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
|
||||
&& matches!(
|
||||
policy,
|
||||
AskForApproval::Reject(reject_config) if reject_config.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,
|
||||
@@ -183,6 +194,52 @@ pub(crate) fn default_exec_approval_requirement(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_protocol::protocol::RejectConfig;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn default_exec_approval_requirement_rejects_sandbox_prompt_when_configured() {
|
||||
let policy = AskForApproval::Reject(RejectConfig {
|
||||
sandbox_approval: true,
|
||||
rules: false,
|
||||
mcp_elicitations: false,
|
||||
});
|
||||
|
||||
let requirement =
|
||||
default_exec_approval_requirement(policy, &SandboxPolicy::new_read_only_policy());
|
||||
|
||||
assert_eq!(
|
||||
requirement,
|
||||
ExecApprovalRequirement::Forbidden {
|
||||
reason: "approval policy rejected sandbox approval prompt".to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_exec_approval_requirement_keeps_prompt_when_sandbox_rejection_is_disabled() {
|
||||
let policy = AskForApproval::Reject(RejectConfig {
|
||||
sandbox_approval: false,
|
||||
rules: true,
|
||||
mcp_elicitations: true,
|
||||
});
|
||||
|
||||
let requirement =
|
||||
default_exec_approval_requirement(policy, &SandboxPolicy::new_read_only_policy());
|
||||
|
||||
assert_eq!(
|
||||
requirement,
|
||||
ExecApprovalRequirement::NeedsApproval {
|
||||
reason: None,
|
||||
proposed_execpolicy_amendment: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum SandboxOverride {
|
||||
NoOverride,
|
||||
@@ -224,7 +281,13 @@ 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(reject_config) => !reject_config.sandbox_approval,
|
||||
}
|
||||
}
|
||||
|
||||
fn start_approval_async<'a>(
|
||||
|
||||
@@ -255,6 +255,28 @@ impl DeveloperInstructions {
|
||||
None => APPROVAL_POLICY_ON_REQUEST_RULE.to_string(),
|
||||
}
|
||||
}
|
||||
AskForApproval::Reject(reject_config) => {
|
||||
let command_prefixes = format_allow_prefixes(exec_policy.get_allowed_prefixes());
|
||||
let on_request_instructions = match command_prefixes {
|
||||
Some(prefixes) => {
|
||||
format!(
|
||||
"{APPROVAL_POLICY_ON_REQUEST_RULE}\n## Approved command prefixes\nThe following prefix rules have already been approved: {prefixes}"
|
||||
)
|
||||
}
|
||||
None => APPROVAL_POLICY_ON_REQUEST_RULE.to_string(),
|
||||
};
|
||||
let sandbox_approval = reject_config.sandbox_approval;
|
||||
let rules = reject_config.rules;
|
||||
let mcp_elicitations = reject_config.mcp_elicitations;
|
||||
format!(
|
||||
"{on_request_instructions}\n\n\
|
||||
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)
|
||||
|
||||
@@ -377,11 +377,41 @@ 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(RejectConfig),
|
||||
|
||||
/// Never ask the user to approve commands. Failures are immediately returned
|
||||
/// to the model, and never escalated to the user for approval.
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema, TS)]
|
||||
pub struct RejectConfig {
|
||||
/// Reject approval prompts related to sandbox escalation.
|
||||
pub sandbox_approval: bool,
|
||||
/// Reject prompts triggered by execpolicy `prompt` rules.
|
||||
pub rules: bool,
|
||||
/// Reject MCP elicitation prompts.
|
||||
pub mcp_elicitations: bool,
|
||||
}
|
||||
|
||||
impl RejectConfig {
|
||||
pub const fn rejects_sandbox_approval(self) -> bool {
|
||||
self.sandbox_approval
|
||||
}
|
||||
|
||||
pub const fn rejects_rules_approval(self) -> bool {
|
||||
self.rules
|
||||
}
|
||||
|
||||
pub const fn rejects_mcp_elicitations(self) -> bool {
|
||||
self.mcp_elicitations
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents whether outbound network access is available to the agent.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, Default, JsonSchema, TS,
|
||||
@@ -2831,6 +2861,26 @@ mod tests {
|
||||
assert!(enabled.has_full_network_access());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reject_config_mcp_elicitation_flag_is_field_driven() {
|
||||
assert!(
|
||||
RejectConfig {
|
||||
sandbox_approval: false,
|
||||
rules: false,
|
||||
mcp_elicitations: true,
|
||||
}
|
||||
.rejects_mcp_elicitations()
|
||||
);
|
||||
assert!(
|
||||
!RejectConfig {
|
||||
sandbox_approval: false,
|
||||
rules: false,
|
||||
mcp_elicitations: false,
|
||||
}
|
||||
.rejects_mcp_elicitations()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_write_restricted_read_access_includes_effective_writable_roots() {
|
||||
let cwd = if cfg!(windows) {
|
||||
|
||||
Reference in New Issue
Block a user