← Back to PRs

#13707: macOS: respect exec-approvals.json settings in gateway prompter

by sliekens open 2026-02-10 22:46 View on GitHub →
app: macos stale
## What The macOS companion app was not respecting `exec-approvals.json` settings and always prompted for approval on every `system.run` request, regardless of the configured security/ask policy. ## Why `ExecApprovalsGatewayPrompter` had three problems: 1. **No exec-approvals.json consultation:** `shouldPresent()` only checked connection mode, session matching, and recent input activity. It never read the `security`/`ask` settings from `exec-approvals.json`, so the app always prompted regardless of policy. 2. **Silent drop instead of resolving:** When `shouldPresent()` returned `false`, the handler returned without resolving the gateway request. The gateway was left waiting for a decision that never came, causing timeouts. 3. **No askFallback handling:** When a prompt was needed but couldn't be presented (e.g., remote + stale activity, session mismatch), there was no fallback behavior — the request was just dropped. ## How - `shouldPresent` now returns a `PresentationDecision` struct with four fields: `shouldAsk`, `canPresent`, `security`, and `askFallback`. - Added `shouldAsk(security:ask:)` to evaluate whether the ask policy requires prompting the user. This is distinct from whether the action is allowed — the `security` policy (full/deny/allowlist) decides the actual outcome. - `shouldPresent` calls `ExecApprovalsStore.resolveReadOnly(agentId:)` using `request.request.agentId` (not `sessionKey`) and checks the per-agent resolved config, which respects the full resolution chain: agent-specific entry → wildcard `*` entry → global defaults. - `resolveReadOnly` is a new read-only variant of `resolve` that uses `loadFile()` instead of `ensureFile()` to avoid disk writes on the MainActor. - Three distinct paths in `handle(push:)`: 1. **Ask policy says no prompt** (`shouldAsk: false`) → resolve based on security policy: allow if `security == .full`, deny otherwise. 2. **Prompt needed but can't present** (`canPresent: false`) → consult `askFallback` (default: `.deny`); only allows if `askFallback == .full`. 3. **Can present** → show the native prompt as before. - Added `_testShouldAsk` test helper and three new test cases covering all `ExecAsk × ExecSecurity` combinations. <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> The macOS companion app now correctly respects `exec-approvals.json` settings when evaluating `system.run` requests. Previously, it always prompted regardless of policy; now it consults the resolved security/ask settings from `exec-approvals.json` (including agent-specific overrides and wildcard entries) and handles three distinct scenarios: auto-approve when policy doesn't require prompting, use `askFallback` when prompt is needed but can't be shown, and present native prompt when conditions allow. The PR addresses two critical bugs identified in previous reviews: - Now uses `request.request.agentId` instead of `sessionKey` when resolving policy, ensuring agent-specific overrides are respected - When prompts can't be shown, consults `askFallback` instead of silently approving The implementation introduces `resolveReadOnly()` in `ExecApprovalsStore` to avoid disk writes on the MainActor, `shouldAsk()` helper to evaluate ask policy against security settings, and a `PresentationDecision` struct to cleanly separate policy evaluation from presentation heuristics. Tests cover all `ExecAsk × ExecSecurity` combinations. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk - Both critical bugs from previous review are properly fixed, logic is clear and correct, comprehensive tests validate all policy combinations, and the read-only resolve pattern avoids potential MainActor disk write issues - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs