Fix repeated Codex native approval prompts after allow-always#78234
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. from source inspection: current main collapses native allow-always into one-shot allow and installs the native permission_request hook by default in Codex app-server approval modes. I did not run the live Telegram/Codex path. Real behavior proof Next step before merge Security Review detailsBest possible solution: Merge this direction after exact-head CI completes or a maintainer accepts the supplied proof, preserving the exact-match in-memory cache and server-side allowed-decision enforcement. Do we have a high-confidence way to reproduce the issue? Yes from source inspection: current main collapses native allow-always into one-shot allow and installs the native permission_request hook by default in Codex app-server approval modes. I did not run the live Telegram/Codex path. Is this the best way to solve the issue? Yes. The patch fixes the implicated relay, Codex app-server wiring, and approval validation surfaces with narrow exact-match scoping and an explicit compatibility opt-in. Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 9c7c0ae891e8. |
This comment was marked as low quality.
This comment was marked as low quality.
884e54c to
fcfdf02
Compare
|
Refreshed the generated Gateway protocol models for Local proof on the new head: pnpm protocol:check
pnpm test src/gateway/server-methods/plugin-approval.test.ts src/infra/plugin-approval-forwarder.test.ts src/plugin-sdk/approval-renderers.test.ts src/agents/harness/native-hook-relay.test.ts extensions/codex/src/app-server/run-attempt.test.ts -- --reporter=dot
git diff --check HEAD~1..HEADThe new exact-head CI run is queued now. |
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
…aw#78234) * fix: reuse codex native approvals * fix: scope native approval reuse by session * fix: let codex guardian own native permission approvals * fix: refresh plugin approval protocol models --------- Co-authored-by: pashpashpash <nik@vault77.ai>
This PR fixes the approval noise that showed up when OpenClaw sessions run through the native Codex harness, especially in guardian mode.
The user-facing problem was simple: Codex would try to do a routine shell action and Telegram would light up with approval cards. That was especially confusing in guardian mode, because guardian is supposed to review most of those requests itself. A command that should have been checked by Codex and then allowed or denied was instead getting surfaced to the human too early.
There were two separate approval paths overlapping.
The first path was the native Codex hook relay. Codex emits a native
PermissionRequestfor things like shell execution, and OpenClaw can relay that into its plugin approval UI. That relay already had anAllow Alwaysbutton, but internally OpenClaw treated it the same asAllow Once: it returned a plain allow decision to Codex and forgot the user's choice. So if a Telegram user allowed the native approval forbrowserforce tabs, then asked for the same command again in the same session, OpenClaw could still ask again.This PR makes that button mean what it says. Native allow-always decisions are remembered for the same session and the same concrete request: same provider, agent, session identity, tool name, cwd, and tool input. A different command, different cwd, different input, or different session still asks again. The cache is intentionally in-memory and short-lived, so it only removes the repetitive prompt without creating broad background permission.
Before:
After:
The second path was Codex app-server approval. In guardian mode, Codex should own the real escalation decision: a sandboxed command fails because it needs network or filesystem access, the agent asks to retry with more permission, guardian reviews the specific request, and Codex either runs it or rejects it. OpenClaw was accidentally cutting in front of that flow by relaying Codex's early native
PermissionRequesthook to Telegram before the app-server reviewer had finished. That is why a safe-looking command could produce a human approval prompt even though guardian would have approved it automatically a moment later.This PR changes the default hook wiring for Codex app-server approval modes. When Codex approvals are active, OpenClaw no longer relays native
permission_requesthooks by default. Codex gets the first chance to review and resolve the escalation. Operators who explicitly need the old compatibility behavior can still opt intonativeHookRelay.events: ["permission_request"].Before, a guardian-mode dev-agent command like this could immediately create a Telegram plugin approval card:
After, the same shape follows the intended Codex path. The sandboxed read fails on network access, the agent retries with escalation, guardian reviews the request, marks it low risk, and the command completes. In the dev-agent smoke test for this PR, no new
plugin.approval.requestwas logged for that Telegram run.There is one small supporting cleanup in the plugin approval layer. Approval requests can now declare which decisions are actually valid, and the gateway enforces that list instead of relying only on which buttons a chat client happens to render. That keeps native approval requests, app-server approval requests, and
/approvereplies aligned.One thing this PR deliberately does not do: it does not change the post-denial override path. In native Codex clients, a guardian denial can be surfaced back to the user as an explicit “approve this denied action” affordance; the client then sends Codex the exact denied action so the model can retry with that approval context. OpenClaw does not wire that post-denial affordance into Telegram in this PR. This change is about removing the premature pre-guardian prompt and making native
Allow Alwaysstick for the request it actually approved.