Support PreToolUse permissionDecision: ask for native approval prompts

Open 💬 6 comments Opened Jun 16, 2026 by jignaciofernandez

Summary

Please add native support for PreToolUse hooks returning permissionDecision: "ask", so a hook can escalate a specific tool call into a human approval prompt.

This was already explored in closed PR #20702: https://github.com/openai/codex/pull/20702

Current behavior

Codex currently parses permissionDecision: "ask", but treats it as unsupported. A PreToolUse hook that returns:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "ask",
    "permissionDecisionReason": "This command needs explicit approval."
  }
}

fails with:

PreToolUse hook returned unsupported permissionDecision:ask

The same applies to some Claude-compatible fields such as continue: false, stopReason, and suppressOutput: they are parsed, reported as unsupported, and the tool call continues depending on the hook failure behavior.

Desired behavior

PreToolUse permissionDecision: "ask" should route that specific tool call to the native Codex human approval flow.

Suggested precedence:

  • PreToolUse: ask should force a user approval prompt for that tool call.
  • PreToolUse: deny should continue to block the tool call.
  • PreToolUse: allow should not bypass core policy, sandbox checks, network checks, or later concrete approval boundaries.
  • Core policy should remain authoritative; a hook should be able to tighten approval requirements, not weaken them.

Why this matters

Hooks are useful as durable safety policy. Today a hook can deny a tool call, but it cannot say “this is allowed only after explicit user approval.” That forces users to either globally tighten approval policy, which is noisy, or deny commands that would be safe with per-call approval.

This is especially useful for Codex App/CLI setups where teams use PreToolUse hooks to guard high-risk commands, writes, network access, production operations, or organization-specific workflows.

Related work

PR #20702 already implemented a design in this direction and described the intended safety property: ask can tighten a decision into explicit approval, while allow must not waive core policy or later checks.

It was closed for inactivity, not because the feature was rejected. Reopening that PR or landing an equivalent focused implementation would solve this request.

View original on GitHub ↗

6 Comments

rpelevin · 2 months ago

I would support this, with one extra invariant: ask should create a pending approval record for the exact tool call, not just flip a hook-output flag.

The hook ask decision is a tightening decision. I would bind it to:

  • tool use id;
  • tool name and runtime;
  • canonical args or effective input digest;
  • hook id or source;
  • ask reason shown to the user;
  • core policy and sandbox state before the hook;
  • approval prompt id;
  • terminal outcome: approved, denied, cancelled, or expired;
  • execution attempt id and result after approval.

The precedence I would want:

  1. deny wins over ask or allow, and produces a terminal non-execution record;
  2. ask routes to native approval even if cached approvals or auto review would otherwise allow;
  3. allow is accepted only as compatibility/no-op and cannot waive core policy, sandbox checks, network checks, or later concrete approval boundaries;
  4. if a hook rewrites input and asks, approval binds the effective input digest, with the original input retained for audit;
  5. retry or resume either reuses the same pending approval record or reruns hooks, but must not silently execute original or stale input.

Regression tests I would pin:

  • ask on an otherwise allowed command prompts exactly once and does not execute before approval;
  • denied approval does not run and records terminal denial;
  • changed args after ask invalidate the prior approval;
  • duplicate or late approval cannot execute a second time;
  • hook allow does not bypass sandbox, network, or core approval policy;
  • multiple hooks resolve deterministically, with deny over ask over allow.

That gives teams a policy hook that can tighten authority for one call without creating an ambient bypass path.

Keesan12 · 2 months ago

The tightening-only rule is the key design constraint here, and it is worth making visible in the hook contract itself.

If ask is supported, the receipt back to the hook layer should say which approval boundary was escalated, whether core policy would already have blocked it, and whether the user approved or denied. That makes hook-driven approvals auditable instead of feeling like a silent second policy engine.

rpelevin · 2 months ago

Yes. I would make that receipt part of the contract, not just an implementation log.

The hook layer needs to know whether it tightened an action into native approval and what happened afterward. Otherwise "ask" becomes hard to audit: the hook can request escalation, but it cannot distinguish "core policy would have blocked anyway", "native approval was shown and approved", "native approval was denied", or "the prompt expired/cancelled".

The receipt I would want back to the hook is small:

  • hook decision id;
  • original tool call id;
  • effective input digest approved by the user;
  • boundary escalated: sandbox, network, file write, command, external service, or other host policy;
  • pre-hook/core-policy disposition;
  • native approval prompt id;
  • terminal outcome: approved, denied, cancelled, expired, or superseded;
  • execution attempt id, only if approval actually led to execution.

Two invariants follow from that:

  • "ask" can only tighten or route to native approval; it cannot weaken core policy.
  • approval of one digest cannot authorize a later rewritten/stale call.

That keeps the hook contract simple: the hook asks for a higher-authority decision, and the runtime returns a receipt proving which exact decision closed which exact pending call.

Vigilans · 11 days ago

Another useful model for PreToolUse: ask is a two-stage review pipeline:

  1. A hook performs deterministic analysis—such as CC Safety Net—and identifies a high-signal operation that deserves additional scrutiny.
  2. It emits ask with a semantic, rule-specific reason.
  3. Codex routes that approval request to the configured reviewer, including Auto Review.

This combines the strengths of both layers:

  • static analysis reliably captures known risky patterns;
  • the hook supplies a precise explanation of what was detected;
  • Auto Review evaluates the operation in its broader task context.

The result could improve safety without requiring a human prompt for every statically flagged operation.

For this use case, it would be valuable for ask to enter Codex's native approval pipeline and follow the effective approvals_reviewer, rather than always bypassing Auto Review.

apt-get-coke · 4 days ago

A minimal additional acceptance case: a PreToolUse hook needs to add deterministic shell lifecycle instrumentation while preserving native command-aware approval. Today, updatedInput is ignored without permissionDecision allow; allow applies the rewrite but bypasses the ordinary approval decision; ask is unsupported; and PermissionRequest cannot rewrite input. A deny-then-retry wrapper preserves execution safety but native approval and reusable prefix rules evaluate the wrapper rather than the original command. Please support updatedInput together with ask so approval binds the effective input digest, retains the original input for audit, and still applies core policy, sandboxing, and normal approval rules.

AlbertZweistein-2 · 4 days ago

Confirmed still present in codex-cli 0.149.0 on Linux.

Reproduction through a real downstream integration:

  1. A PreToolUse hook classifies git push origin main as an ask-class action.
  2. The user explicitly approves that exact command in chat.
  3. The agent retries that exact command.
  4. The retried hook invocation has no trustworthy one-shot approval state. Because permissionDecision ask is unsupported, the downstream guard must deny again to fail closed.

Observed result: the exact retry remains blocked even though the user approval is explicit. This makes conversational approval non-actionable for hook-gated outward-facing actions. Claude Code routes the same hook-level ask into a native prompt.

A safe fix needs permissionDecision ask to enter the native approval flow, or an unforgeable single-use approval grant bound to the exact retried tool call. Parsing conversation text or trusting an inherited environment variable would create a bypass.

Downstream impact: dev-baseline currently maps ask to deny on Codex and instructs the operator to run the approved command directly.