Repository navigation
fix(codex): debounce auto-resolved 'Approve for me' approval notifications (#8387) - #8519
Conversation
Codex fires its PermissionRequest hook at the human-input boundary before the approval decision, so under 'Approve for me' the review agent approves and Codex resumes within ~1s. The completion coordinator dispatched the OS attention notification immediately on that 'waiting'/'blocked' hook, so the self-resolving pause raised a false 'approval required' notification. Debounce the Codex OS attention notification behind a 1500ms quiet window and cancel it when a working/completion hook lands inside the window. Scoped strictly to agentType==='codex'; every other agent's pause still notifies immediately. The visual sidebar status is set upstream (store.setAgentStatus) before the coordinator runs, so it stays immediate. Co-authored-by: Orca <help@stably.ai>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe coordinator adds a 1.5-second Codex attention quiet window and tracks its pending timer. Codex attention callbacks are delayed while non-Codex callbacks remain immediate. Pending notifications are canceled when work resumes, completion occurs, state resets, or the coordinator is disposed. Tests cover delayed and canceled Codex notifications, immediate non-Codex notifications, and shared Codex permission-pause handling for waiting and blocked states. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…e resume Two adversarial-review hardening fixes to the Codex attention debounce, both preserving the fail-open contract (a genuine 'needs input' pause must always notify): - handleProcessInspectionResult: extend the evidence-teardown guard to also hold while a pendingCodexAttentionTimer is armed. A transient null/shell foreground blip (or a remote/SSH inspection that returns null foreground) could otherwise tear down agent evidence mid-window and make the timer's hasAgentRunEvidence guard silently drop a genuine pause banner — or fire a false process-exit completion racing the pause. Mirrors the pendingHookDone guard. - recordTitleWorking: cancel the debounced attention on a genuine title-driven resume (placed after the replay guard, so a stale post-completion title replay never drops a still-pending genuine pause banner). Adds 6 discriminating fake-timer tests (guard, title resume, blocked parity, done cancels attention, dispose clears timer, second distinct pause re-arms). Co-authored-by: Orca <help@stably.ai>
Adversarial hardening review — 3 rounds, fresh reviewer eachRan three hostile review passes over the Codex attention debounce. The core design (debounce the OS "approval required" banner behind a 1500 ms quiet window, scoped to Fixed
Tests+7 discriminating fake-timer tests (each verified to fail if its guard is reverted): evidence-teardown blip guard, title-resume cancel, Verdicts by dimension
Accepted trade-offs (not changed)
|
…tions (stablyai#8387) (stablyai#8519) * fix(codex): debounce auto-resolved approval notifications (stablyai#8387) Codex fires its PermissionRequest hook at the human-input boundary before the approval decision, so under 'Approve for me' the review agent approves and Codex resumes within ~1s. The completion coordinator dispatched the OS attention notification immediately on that 'waiting'/'blocked' hook, so the self-resolving pause raised a false 'approval required' notification. Debounce the Codex OS attention notification behind a 1500ms quiet window and cancel it when a working/completion hook lands inside the window. Scoped strictly to agentType==='codex'; every other agent's pause still notifies immediately. The visual sidebar status is set upstream (store.setAgentStatus) before the coordinator runs, so it stays immediate. Co-authored-by: Orca <help@stably.ai> * review: harden stablyai#8387 fix — protect debounce evidence + cancel on title resume Two adversarial-review hardening fixes to the Codex attention debounce, both preserving the fail-open contract (a genuine 'needs input' pause must always notify): - handleProcessInspectionResult: extend the evidence-teardown guard to also hold while a pendingCodexAttentionTimer is armed. A transient null/shell foreground blip (or a remote/SSH inspection that returns null foreground) could otherwise tear down agent evidence mid-window and make the timer's hasAgentRunEvidence guard silently drop a genuine pause banner — or fire a false process-exit completion racing the pause. Mirrors the pendingHookDone guard. - recordTitleWorking: cancel the debounced attention on a genuine title-driven resume (placed after the replay guard, so a stale post-completion title replay never drops a still-pending genuine pause banner). Adds 6 discriminating fake-timer tests (guard, title resume, blocked parity, done cancels attention, dispose clears timer, second distinct pause re-arms). Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
Summary
Codex under "Approve for me" no longer raises false "approval required" desktop notifications. When Codex pauses for a permission decision that its auto-review agent then approves, the pane resumes on its own and no attention notification fires. A permission pause that a human actually needs to resolve still notifies — just after a short quiet window.
Root cause
Codex emits its
PermissionRequesthook at the human-input boundary before the approval decision is made. That hook maps unconditionally tostate: 'waiting'(src/shared/agent-hook-listener.ts:3129-3130), and the completion coordinator dispatched the OS attention notification immediately on anywaiting/blockedhook:src/renderer/src/components/terminal-pane/agent-completion-coordinator.ts—observeHookStatus→dispatchAttention()fired synchronously with no debounce.Under "Approve for me", the review agent approves and Codex resumes (a
workinghook) a moment later — but the attention notification had already fired, producing a false alarm. The existing yolo suppressor (codex-auto-approval-notification-suppression.ts) only covers full-auto launch flags, not the "Approve for me" review-agent path, so those pauses fell through to the immediate notification.The fix
Debounce only the Codex OS attention notification behind a
CODEX_ATTENTION_QUIET_MS = 1500msquiet window, mirroring the coordinator's existingHOOK_DONE_QUIET_MSpattern:waiting/blockedattention hook, schedule the notification instead of firing it. Aworkingor completion hook arriving inside the window cancels the pending dispatch.workinghook, the completion-hook branch, the suppressed-attention path, the internaldispatchCompletion(covers title/process-exit completion sources),resetCompletionState, anddispose.isLive()andhasAgentRunEvidencebefore firing, so a torn-down pane can never notify.Why zero-regression:
payload.agentType === 'codex'. Every other agent'swaiting/blockedpause dispatches immediately, exactly as before (asserted by a test that also checks no timer is armed for non-Codex).store.setAgentStatusat the IPC ingestion layer (useIpcEvents.ts:3096) before the coordinator's notification path runs (:3125), so the "needs input" indicator is unaffected by the debounce. The coordinator only gates the OS notification.src/shared/agent-hook-listener.ts, so the prior fix fix(agent-status): clear answered Claude question waits #8311 (Claude sticky waits scoped toPermissionRequest) is untouched.Evidence
UI screenshots aren't feasible in headless CI; the co-located fake-timer tests are the authoritative proof.
Before (fix reverted, tests present) — the immediate-dispatch bug:
After (fix applied):
pnpm run typecheck:tsc:web→ exit 0.oxlinton all changed files → exit 0.The three coordinator tests prove: (a)
working → waiting(codex) → workinginside the window ⇒ attention not dispatched (canceled); (b)working → waiting(codex)then advance past the window ⇒ attention is dispatched with the exactcodexpayload; (c) a non-Codexwaitingdispatches immediately and arms no timer.ELI5
Codex raises its hand ("can I run this?") before the robot reviewer decides. Orca used to buzz your phone the instant the hand went up. But with "Approve for me", the reviewer says "yes" a second later and Codex keeps going — so the buzz was pointless. Now Orca waits about a second and a half; if Codex is already back to work, it stays quiet. If Codex is genuinely stuck waiting for you, the buzz still comes through.
Trade-offs
working/completion hook after an auto-approval, the notification still fires (fail-open) once the window elapses — no notification is ever lost, only debounced.Relationship to existing PR
Connected PR #8443 by @rodboev independently lands the same core approach (Codex-scoped 1500ms quiet window; non-Codex immediate; yolo still suppressed by the existing predicate; change kept inside the coordinator that owns notification cadence). Credit to @rodboev — the diagnosis and design match. I implemented my own version before reading theirs and then compared.
Two deliberate differences in this PR:
dispatchAttention(including the per-panedispatchHookLifecyclecursor/cache effect) through the timer; here only the OS notification is deferred, keeping every non-notification effect immediate.agent-hook-completion-notifications.test.tsneed to advance past the quiet window. fix(codex): debounce auto-resolved approval notifications (#8387) #8443 doesn't touch that file, so those integration tests would fail on its branch. This PR updates them (and factors their shared setup into one helper to stay under themax-lineslimit without a suppression).Recommendation: land whichever review prefers; the two are functionally equivalent for the real "Approve for me" scenario. This PR is the more complete/self-contained of the two.
Review loops
2 fresh independent reviewer passes (Opus). Round 1: CLEAN with two non-blocking nits (belt-and-suspenders cancel on non-hook completion sources; strengthen the non-Codex test with a timer-count assertion) — both applied. Round 2: CLEAN.
Fixes #8387
Made with Orca 🐋