fix(agent-status): clear answered Claude and Codex waits#8383
Conversation
…ionRequest Claude's AskUserQuestion is auto-allowed, but in addition to the PreToolUse hook it emits a PermissionRequest ~120ms later. Orca registers PermissionRequest, so that event overwrites the PreToolUse as the cached `previous` status — and it carries no tool_use_id. shouldKeepClaudePermissionVisible (from stablyai#8311) keeps a Claude PermissionRequest wait sticky until a resuming-tool id match clears it. Since the AskUserQuestion PermissionRequest exposes no tool_use_id, the answer's PostToolUse 'working' hook can never satisfy that match, so the sidebar row stays pinned on the amber "waiting" attention state until the turn's Stop flips it straight to done. Users see the row appear stuck from the moment they answer until the whole reply finishes. stablyai#8311 only covered the PreToolUse-attributed wait; the PermissionRequest that lands right after it slips through. Treat any PermissionRequest whose tool is an AskUserQuestion variant as an interactive question rather than an Allow/Deny gate, so its wait clears on the next 'working' hook. Real tool permission requests (Bash, etc.) stay sticky as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCLYfTSDnDyBgotAp8yRoV
📝 WalkthroughWalkthroughExports 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/main/agent-hooks/server.codex-permission-resume.test.ts (1)
28-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd tests for untested guard conditions in
resumeCodexPermissionWaitFromTerminalTitle.Two of the four guard conditions in the resume method are not exercised: the staleness check (
Date.now() - existing.receivedAt > AGENT_STATUS_STALE_AFTER_MS) and thehookEventName !== 'PermissionRequest'check for a Codexwaitingentry. Adding these would confirm the guards reject stale and non-permission waits.🧪 Suggested additional test cases
import { describe, expect, it, vi } from 'vitest' import { makePaneKey } from '../../shared/stable-pane-id' +import { AGENT_STATUS_STALE_AFTER_MS } from '../../shared/agent-status-types' import { AgentHookServer } from './server'it('ignores other agents and Codex states that are not permission waits', () => { // ... existing test body ... }) + + it('ignores stale Codex permission waits', () => { + vi.useFakeTimers() + const server = new AgentHookServer() + ingestCodexPermission(server) + vi.advanceTimersByTime(AGENT_STATUS_STALE_AFTER_MS + 1) + expect(server.resumeCodexPermissionWaitFromTerminalTitle(PANE_KEY)).toBe(false) + vi.useRealTimers() + }) + + it('ignores Codex waiting states without a PermissionRequest hook event', () => { + const server = new AgentHookServer() + server.ingestRemote( + { + paneKey: PANE_KEY, + hookEventName: 'AskUserQuestion', + payload: { state: 'waiting', prompt: 'Answer me', agentType: 'codex' } + }, + 'connection-1' + ) + expect(server.resumeCodexPermissionWaitFromTerminalTitle(PANE_KEY)).toBe(false) + })src/main/runtime/orca-runtime.ts (1)
6207-6210: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winResume-wait hook fires on every "working" title tick, not just on the transition into working.
agentStatusis computed from the raw title andresumeCodexPermissionWaitForPtyis invoked whenever it equals'working', but this happens beforeprevStatus(pty.lastAgentStatus) is read a few lines below. Spinner-frame titles retriggeronTitlemany times per second while an agent is working (per comments elsewhere in this file, "synthetic spinner ticks arrive ~12.5x/sec per working pane"), so this hook is invoked repeatedly for the entire duration of the working state instead of once on the working transition. Moving the check afterprevStatusis known and gating onprevStatus !== 'working'would avoid the redundant repeated calls while still satisfying the "resume on next working hook" requirement from the PR description.♻️ Proposed fix: gate on transition into working
const agentStatus = detectAgentStatusFromTitle(rawTitle) - if (agentStatus === 'working') { - this.resumeCodexPermissionWaitForPty(ptyId) - } let ptyRecordChanged = false const pty = this.ptysById.get(ptyId) if (pty) { const prevStatus = pty.lastAgentStatus const prevTitle = pty.lastOscTitle + if (agentStatus === 'working' && prevStatus !== 'working') { + this.resumeCodexPermissionWaitForPty(ptyId) + } const observedAt = this.nextTitleObservationSequence()Also applies to: 6279-6296
src/main/runtime/orca-runtime.test.ts (1)
6975-6986: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueTest is well-isolated; consider relocating out of the unrelated describe block.
The new test doesn't exercise the "terminal side-effect fact channel" (no
onTerminalSideEffects/batches usage) — it tests Codex permission-resume routing via a separateresumeCodexPermissionWaitdependency. Consider moving it to its owndescribeblock (or alongside other Codex-permission-resume tests) for discoverability, since this describe block is otherwise scoped topty:sideEffectbatch behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: b46fe951-2241-4107-8ed9-e27cc1b82788
📒 Files selected for processing (6)
src/main/agent-hooks/server.codex-permission-resume.test.tssrc/main/agent-hooks/server.tssrc/main/index.tssrc/main/runtime/orca-runtime.test.tssrc/main/runtime/orca-runtime.tssrc/shared/agent-status-types.ts
Summary
Fixes two related agent-status waits that stayed amber after the user answered:
AskUserQuestioncan emit a trailingPermissionRequestwithout atool_use_id, so Orca could not match the answer to the waiting tool.PermissionRequest, but Codex 0.144.1 emits no execution-start hook after approval. Its next hook isPostToolUse, after the command finishes, so the row stayed waiting throughout execution.Fix
AskUserQuestionpermission events as interactive questions so the next working hook clears the wait.PermissionRequest, synthesize the missing waiting-to-working transition in the hook-status owner.PermissionRequestwaits; other agents and states are unchanged.The Codex transition lives in the main runtime and hook server, so desktop, parked terminals,
worktree ps, headless serve, and SSH/remote runtimes read the same corrected state.Root cause
Claude and Codex expose different incomplete lifecycle sequences around user input. Claude supplies an answer hook but its preceding
AskUserQuestionpermission event lacks the identifier used by the sticky-permission guard. Codex supplies the permission hook but no hook when approval resumes execution. In both cases Orca retained a valid waiting event after the terminal had already resumed.Screenshots
No layout or styling change. The visible behavior changes from an amber waiting dot remaining during command execution to the normal working indicator immediately after approval.
Testing
pnpm run lintpnpm run typecheck:webpnpm run check:max-lines-ratchetgit diff --checkLive Codex validation used an on-request session and an approved
sleep 15command:Action Required;worktree psreportedwaiting.working.PostToolUseand completion: the row moved todonewithMAIN_FIX_COMPLETE.AI Review Report
CodeRabbit review feedback was applied:
working, not on every spinner frame.PermissionRequest.No conflicting reviewer guidance or unresolved inline review threads remain.
Security Audit
No new command execution, authentication, filesystem access, dependency, or public IPC surface was added. The fallback only transforms an already-normalized, fresh Codex status row after a pane-scoped runtime title transition. Existing payload caps and pane-key validation remain in force.
Compatibility and risk
Notes