Skip to content

Persist and broadcast interactive poll/plan resolution#156

Open
GrigoryPervakov wants to merge 1 commit into
mainfrom
grisha/web-poll-answered-state
Open

Persist and broadcast interactive poll/plan resolution#156
GrigoryPervakov wants to merge 1 commit into
mainfrom
grisha/web-poll-answered-state

Conversation

@GrigoryPervakov

Copy link
Copy Markdown
Member

Summary

  • Answered AskUserQuestion polls and ExitPlanMode/EnterPlanMode approvals now persist their resolved state: reloading a session renders them read-only instead of re-prompting, and clicking a resolved poll no longer posts a stray chat message.
  • The backend broadcasts a new interaction_resolved event (buffered for reconnect replay) when an interaction settles, so parallel clients and mid-turn reconnects clear the pending prompt instead of soliciting an already-given answer.
  • Resolved polls re-highlight the chosen options from the persisted tool_result; an interaction that ends without an answer (timeout / cancel / deny) renders "Closed".

Why

AskUserQuestion/plan tools pause the agent mid-turn while the web UI collects an answer. The answered state lived only in ephemeral React state and was never broadcast, so (a) a reload re-rendered the poll as a blank interactive form whose click posted the answer as a brand-new message, and (b) other connected clients / a post-reconnect client kept showing the live prompt.

Test plan

  • Answer an in-chat poll → locks to read-only "Answered" with selections highlighted
  • Reload the session → stays answered, no re-prompt, no stray message
  • Open the chat in two tabs → answering in one locks the other (broadcast)
  • Reload while a poll is still pending → comes back interactive, not duplicated
  • Plan approval shows the same reload / parallel-client behavior
  • pytest tests/test_interactive.py green; npm run build clean

🤖 Generated with Claude Code

Reloading a session re-rendered an already-answered AskUserQuestion poll as a
fresh interactive form, and clicking it posted a stray chat message. The
resolved state was never broadcast either, so parallel clients and mid-turn
reconnects kept soliciting an already-given answer.

Backend:
- broadcast an interaction_resolved event when an interaction settles
  (answer/deny/timeout/cancel), buffered for reconnect replay

Frontend:
- QuestionBlock renders read-only from the persisted tool_result, re-highlights
  the chosen options, and drops the sendMessage fallback that posted answers as
  new chat messages
- both poll and plan blocks lock once a seen prompt clears (parallel client /
  pre-reconnect-replay window)
- clear pendingInteraction on interaction_resolved and skip re-restoring a
  resolved interaction during reconnect buffer replay
- show "Closed" instead of "Answered" when an interaction ends without an answer

Adds a test for the resolution broadcast.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@GrigoryPervakov GrigoryPervakov requested a review from pufit June 29, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant