Skip to content

feat: add Reject approval policy with granular prompt rejection controls#12087

Merged
bolinfest merged 1 commit intomainfrom
pr12087
Feb 19, 2026
Merged

feat: add Reject approval policy with granular prompt rejection controls#12087
bolinfest merged 1 commit intomainfrom
pr12087

Conversation

@bolinfest
Copy link
Collaborator

@bolinfest bolinfest commented Feb 18, 2026

Why

We need a way to auto-reject specific approval prompt categories without switching all approvals off.

The goal is to let users independently control:

  • sandbox escalation approvals,
  • execpolicy prompt rule approvals,
  • MCP elicitation prompts.

What changed

  • Added a new primary approval mode in protocol/src/protocol.rs:
pub enum AskForApproval {
    // ...
    Reject(RejectConfig),
    // ...
}

pub struct RejectConfig {
    pub sandbox_approval: bool,
    pub rules: bool,
    pub mcp_elicitations: bool,
}
  • Wired RejectConfig semantics through approval paths in core:

    • core/src/exec_policy.rs
      • rejects rule-driven prompts when rules = true
      • rejects sandbox/escalation prompts when sandbox_approval = true
      • preserves rule priority when both rule and sandbox prompt conditions are present
    • core/src/tools/sandboxing.rs
      • applies sandbox_approval to default exec approval decisions and sandbox-failure retry gating
    • core/src/safety.rs
      • keeps Reject { all false } behavior aligned with OnRequest for patch safety
      • rejects out-of-root patch approvals when sandbox_approval = true
    • core/src/mcp_connection_manager.rs
      • auto-declines MCP elicitations when mcp_elicitations = true
  • Ensured approval policy used by MCP elicitation flow stays in sync with constrained session policy updates.

  • Updated app-server v2 conversions and generated schema/TypeScript artifacts for the new Reject shape.

Verification

Added focused unit coverage for the new behavior in:

  • core/src/exec_policy.rs
  • core/src/tools/sandboxing.rs
  • core/src/mcp_connection_manager.rs
  • core/src/safety.rs
  • core/src/tools/runtimes/apply_patch.rs

Key cases covered include rule-vs-sandbox prompt precedence, MCP auto-decline behavior, and patch/sandbox retry behavior under RejectConfig.

@bolinfest bolinfest changed the title feat: introduce Reject variant of AskForApproval feat: add AskForApproval::Reject with granular prompt controls Feb 18, 2026
Copy link
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6022174e39

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AskForApproval } from "./AskForApproval";
import type { AskForApprovalProtocolV1 } from "./AskForApprovalProtocolV1";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Generate AskForApprovalProtocolV1 TypeScript definition

This generated import references ./AskForApprovalProtocolV1, but that file is missing from codex-rs/app-server-protocol/schema/typescript (only AskForApproval.ts and v2/AskForApproval.ts exist), so downstream TypeScript compilation will fail on module resolution. The same missing type is also re-exported from index.ts, so the v1 schema bundle is currently broken for consumers.

Useful? React with 👍 / 👎.

Comment on lines 384 to 386
Reject {
/// Reject approval prompts related to sandbox escalation.
sandbox_approval: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve string reject deserialization for approval_policy

Adding Reject as a data-carrying enum variant means serde now expects an object payload for that variant, so a plain approval_policy = "reject" no longer deserializes when ConfigToml reads Option<AskForApproval> (core/src/config/mod.rs). This conflicts with the new backward-compatibility intent documented in reject_defaults() and the updated config schema that advertises string "reject", and will cause config parsing failures for users who follow that format.

Useful? React with 👍 / 👎.

Comment on lines 58 to 61
|| matches!(
policy,
AskForApproval::OnFailure | AskForApproval::Reject { .. }
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip prompt path when rejecting sandbox approvals

This condition now routes every AskForApproval::Reject { .. } through the first branch, which can return AskUser when no platform sandbox is available, so the explicit reject behavior at lines 84-95 is never reached for reject policies. In practice, Reject { sandbox_approval: true } can still surface approval prompts (for example on Windows with sandboxing disabled) instead of being auto-rejected.

Useful? React with 👍 / 👎.

Comment on lines 1831 to 1835
self.services
.mcp_connection_manager
.read()
.await
.set_approval_policy(session_configuration.approval_policy.value());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reapply MCP approval policy after manager reinitialization

The policy is only pushed into McpConnectionManager here during turn creation, but MCP refresh/startup paths recreate the manager with default state (AskForApproval::OnRequest in ElicitationRequestManager::default). After a refresh (and before the first turn), Reject { mcp_elicitations: true } is silently lost, so elicitations can prompt instead of auto-declining.

Useful? React with 👍 / 👎.

Copy link
Collaborator

@dylan-hurd-oai dylan-hurd-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couple of non-blocking comments!

Decision::Allow
}
}
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be great to have tests for this piece in particular

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added!

matches!(self, Self::Reject { rules: true, .. })
}

pub const fn rejects_mcp_elicitations(self) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd have some product questions to sort out before actually enforcing this, but should this also match on Never?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this, these methods are specific to the Reject variant, so I introduced the RejectConfig struct and moved these methods to RejectConfig.

impl AskForApproval {
/// Backward-compatible defaults for `approval_policy = "reject"` in
/// `config.toml`.
pub const fn reject_defaults() -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we use new_workspace_write_policy() etc. in SandboxPolicy, should we use a similar naming convention here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@bolinfest bolinfest changed the title feat: add AskForApproval::Reject with granular prompt controls feat: add granular reject approval controls Feb 19, 2026
@bolinfest bolinfest force-pushed the pr12087 branch 6 times, most recently from 34f85fb to c132a87 Compare February 19, 2026 01:32
@bolinfest bolinfest changed the title feat: add granular reject approval controls feat: add Reject approval policy with granular prompt rejection controls Feb 19, 2026
@bolinfest bolinfest force-pushed the pr12087 branch 3 times, most recently from 1980c97 to 714342d Compare February 19, 2026 19:00
@bolinfest bolinfest merged commit 425fff7 into main Feb 19, 2026
61 of 73 checks passed
@bolinfest bolinfest deleted the pr12087 branch February 19, 2026 19:41
@github-actions github-actions bot locked and limited conversation to collaborators Feb 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants