Repository navigation
fix(agent-status): clear answered Claude question waits - #8311
Merged
Merged
Conversation
mmarabel
marked this pull request as ready for review
July 11, 2026 18:43
Contributor
📝 WalkthroughWalkthroughClaude permission visibility now remains sticky only when the previous hook was a 🚥 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 |
nwparker
approved these changes
Jul 11, 2026
nwparker
left a comment
Contributor
There was a problem hiding this comment.
Thanks — this is exactly the right scope for the stuck needs-attention bug.
Reviewed and verified end to end:
- Root cause confirmed: the sticky guard exists to protect PermissionRequest waits (no tool_use_id), and was over-applied to every Claude waiting→working transition, trapping answered AskUserQuestion waits. Scoping it by originating hook event is the correct fix, and it strictly narrows stickiness — a genuine permission wait still survives unrelated subagent activity.
- Rebased on latest main locally: the new test goes red when the server change is reverted, and the three affected suites (319 tests) pass with it. Typecheck/lint/format/ratchet clean.
- Verified live in a dev build with a real Claude session plus a replay of the exact hook sequence through the local HTTP ingestion path: question pending shows the amber needs-attention dot; an unrelated Read working hook now flips the sidebar to the working spinner (previously stuck until Esc); a real PermissionRequest stays amber through unrelated tool work; Stop resolves to done.
6 tasks done
Contributor
Author
6 of 9 tasks
brennanb2025
added a commit
that referenced
this pull request
Jul 16, 2026
An answered AskUserQuestion left the amber "waiting" indicator on sidebar rows and tabs until the agent's next tool hook or turn end — unbounded linger while the model thinks or streams after the answer (measured 17s for a 1000-word reply, 44s for 3000 words). Root cause is an event-shape change: newer Claude reports the AskUserQuestion wait as PermissionRequest (not the PreToolUse shape #7852 special-cased), so the wait inherited real-permission stickiness and shouldKeepClaudePermissionVisible swallowed the answer-time PostToolUse(AskUserQuestion) working event — the identity match can never succeed because the question's PermissionRequest carries no inheritable tool_use_id. That silently undid #8311 for questions. Two scoped changes, both keyed on the tool name rather than the hook event name: - Sticky permission hold now exempts AskUserQuestion waits, so the real answer-time hook (when Claude sends one) clears the wait as #8311 intended. - New guarded inference for the hook Claude may never send: the submit keystroke (Enter or digit quick-select) into a pane whose fresh status is a waiting AskUserQuestion synthesizes the post-answer state, exactly mirroring the existing interrupt inference (renderer baseline capture, main-process re-validation, listener lead-state sync so child-driven refreshes cannot resurrect the dismissed question). Real permission waits (other tools) keep their sticky semantics; batched input and pastes never match the submit classifier. Verified live against a real claude CLI: waiting -> working within ~50ms of both Enter and digit answers, question card dropped, unanswered questions still hold amber, permission stickiness covered by tests.
brennanb2025
added a commit
that referenced
this pull request
Jul 17, 2026
#9074) * fix(agent-status): clear answered Claude question waits at answer time An answered AskUserQuestion left the amber "waiting" indicator on sidebar rows and tabs until the agent's next tool hook or turn end — unbounded linger while the model thinks or streams after the answer (measured 17s for a 1000-word reply, 44s for 3000 words). Root cause is an event-shape change: newer Claude reports the AskUserQuestion wait as PermissionRequest (not the PreToolUse shape #7852 special-cased), so the wait inherited real-permission stickiness and shouldKeepClaudePermissionVisible swallowed the answer-time PostToolUse(AskUserQuestion) working event — the identity match can never succeed because the question's PermissionRequest carries no inheritable tool_use_id. That silently undid #8311 for questions. Two scoped changes, both keyed on the tool name rather than the hook event name: - Sticky permission hold now exempts AskUserQuestion waits, so the real answer-time hook (when Claude sends one) clears the wait as #8311 intended. - New guarded inference for the hook Claude may never send: the submit keystroke (Enter or digit quick-select) into a pane whose fresh status is a waiting AskUserQuestion synthesizes the post-answer state, exactly mirroring the existing interrupt inference (renderer baseline capture, main-process re-validation, listener lead-state sync so child-driven refreshes cannot resurrect the dismissed question). Real permission waits (other tools) keep their sticky semantics; batched input and pastes never match the submit classifier. Verified live against a real claude CLI: waiting -> working within ~50ms of both Enter and digit answers, question card dropped, unanswered questions still hold amber, permission stickiness covered by tests. * fix(agent-status): guard question answer inference Keep multi-question, multi-select, and free-text selector interactions waiting until the full prompt is submitted. Wire native-chat answers into the same guarded inference only after every paced runtime write succeeds, with cancellation and delivery-failure coverage. * chore(skills): refresh manifest for rc.2 * fix(agent-status): verify native chat answer delivery * fix(agent-status): await verified question delivery * fix(agent-status): pin native-chat answer baseline before delivery The native-chat question-answered inference read the live pane status at settle time (after the paced send + remote acceptance, which can span seconds on SSH). If a replacement AskUserQuestion became current in that window, the settle callback minted a fresh baseline from the new question and the server cleared *its* wait — dismissing a question the user never answered. Capture the answered question's baseline before delivery and have the inference getter return it, so the server re-validates against the pinned baseline and rejects a changed status — the same capture-then-revalidate contract the terminal keystroke path already uses. Also hoist the shouldStepNativeChatAskAnswer predicate to a single evaluation. Regression test swaps the live status between sendAnswer and settle and asserts the answered question's baseline is used (fails against the prior live-read getter).
6 tasks
mmarabel
added a commit
to mmarabel/orca
that referenced
this pull request
Aug 15, 2026
…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. 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
enrick-dev
pushed a commit
to enrick-dev/orca
that referenced
this pull request
Oct 15, 2026
enrick-dev
pushed a commit
to enrick-dev/orca
that referenced
this pull request
Oct 15, 2026
stablyai#9074) * fix(agent-status): clear answered Claude question waits at answer time An answered AskUserQuestion left the amber "waiting" indicator on sidebar rows and tabs until the agent's next tool hook or turn end — unbounded linger while the model thinks or streams after the answer (measured 17s for a 1000-word reply, 44s for 3000 words). Root cause is an event-shape change: newer Claude reports the AskUserQuestion wait as PermissionRequest (not the PreToolUse shape stablyai#7852 special-cased), so the wait inherited real-permission stickiness and shouldKeepClaudePermissionVisible swallowed the answer-time PostToolUse(AskUserQuestion) working event — the identity match can never succeed because the question's PermissionRequest carries no inheritable tool_use_id. That silently undid stablyai#8311 for questions. Two scoped changes, both keyed on the tool name rather than the hook event name: - Sticky permission hold now exempts AskUserQuestion waits, so the real answer-time hook (when Claude sends one) clears the wait as stablyai#8311 intended. - New guarded inference for the hook Claude may never send: the submit keystroke (Enter or digit quick-select) into a pane whose fresh status is a waiting AskUserQuestion synthesizes the post-answer state, exactly mirroring the existing interrupt inference (renderer baseline capture, main-process re-validation, listener lead-state sync so child-driven refreshes cannot resurrect the dismissed question). Real permission waits (other tools) keep their sticky semantics; batched input and pastes never match the submit classifier. Verified live against a real claude CLI: waiting -> working within ~50ms of both Enter and digit answers, question card dropped, unanswered questions still hold amber, permission stickiness covered by tests. * fix(agent-status): guard question answer inference Keep multi-question, multi-select, and free-text selector interactions waiting until the full prompt is submitted. Wire native-chat answers into the same guarded inference only after every paced runtime write succeeds, with cancellation and delivery-failure coverage. * chore(skills): refresh manifest for rc.2 * fix(agent-status): verify native chat answer delivery * fix(agent-status): await verified question delivery * fix(agent-status): pin native-chat answer baseline before delivery The native-chat question-answered inference read the live pane status at settle time (after the paced send + remote acceptance, which can span seconds on SSH). If a replacement AskUserQuestion became current in that window, the settle callback minted a fresh baseline from the new question and the server cleared *its* wait — dismissing a question the user never answered. Capture the answered question's baseline before delivery and have the inference getter return it, so the server re-validates against the pinned baseline and rejects a changed status — the same capture-then-revalidate contract the terminal keystroke path already uses. Also hoist the shouldStepNativeChatAskAnswer predicate to a single evaluation. Regression test swaps the live status between sendAnswer and settle and asserts the answered question's baseline is used (fails against the prior live-read getter).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Clear Claude's needs-attention state after the user answers an interactive question and subsequent tool work begins.
AskUserQuestionwaits arrive asPreToolUse, but the hook server previously treated every Claudewaiting → workingtransition as a sticky permission request. A laterRead,Bash, orEditevent therefore could not replace the stale wait unless it matched the original tool identity.This change limits sticky retention to actual
PermissionRequestevents. Answered interactive questions can return to the existing working state, while genuine permission requests remain visible during unrelated tool activity.Closes #8310.
Screenshots
No visual change. The existing animated working indicator now resumes at the correct state transition.
Prior bug captured example:

Testing
pnpm lintpnpm typecheckpnpm testpnpm buildFocused validation completed with Node 24:
pnpm exec vitest run --config config/vitest.config.ts src/main/agent-hooks/server.claude-interactive-question.test.ts src/main/agent-hooks/server.test.ts src/shared/agent-hook-listener.test.ts --maxWorkers=1— 319 tests passedpnpm run typecheck:nodepnpm exec oxfmt --check src/main/agent-hooks/server.ts src/main/agent-hooks/server.claude-interactive-question.test.tspnpm exec oxlint src/main/agent-hooks/server.ts src/main/agent-hooks/server.claude-interactive-question.test.tspnpm run check:max-lines-ratchetThe full repository lint, typecheck, test, and build commands were not run locally; the corresponding checklist items remain unchecked.
AI Review Report
Reviewed the final two-file diff and the surrounding Claude permission-retention logic.
applyNormalizedStatusguard. The regression test exercises the remote ingestion path; the existing shared normalization and server suites cover the local normalization and permission behavior.agentType === 'claude'. Other supported agents and integrations are unchanged. A control test verifies that genuine ClaudePermissionRequeststate remains sticky during unrelated tool work.waiting → workingtransitions. No polling, allocation, or rendering work was added.No additional issues were found during review.
Security Audit
The change introduces no new commands, filesystem paths, dependencies, authentication behavior, secrets, network requests, or IPC surfaces. It compares the already-normalized
hookEventNameagainst the existingPermissionRequestevent name before deciding whether to retain cached status. Input validation and trust-boundary normalization remain unchanged.No security follow-up is needed.
Notes