Skip to content

zarvis: dismiss stale approval prompts when another client answers#345

Merged
edwin-zvs merged 1 commit into
mainfrom
webui-approval-dialog-dismiss
Jun 5, 2026
Merged

zarvis: dismiss stale approval prompts when another client answers#345
edwin-zvs merged 1 commit into
mainfrom
webui-approval-dialog-dismiss

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Bug

When a zarvis session shows a tool-approval prompt, the web UI shows an approval dialog. If the user answers the prompt from the TUI (or the session's PTY, or another browser), the web dialog stays open and never closes — the decision was already made, but the passive copy lingers.

Root cause

The daemon broadcasts ToolApprovalRequest to all clients, but there was no event signaling that an approval was resolved. A client only closed its prompt when it made the decision; it never learned that another client answered.

Fix

Add a transient SessionEvent::ToolApprovalResolved { call_id }:

  • zarvis adapter (interactive.rs + agent.rs) emits it at every approval exit — approve / deny / auto-review→approve / unsafe-auto / stop / interrupt — so it fires regardless of how the prompt was answered (PTY keystroke or tool_decision RPC).
  • daemon (session.rs) broadcasts it live but never persists it to the transcript (same fast-path treatment as BrowserPreview / AgentStatus).
  • web UI (index.html) — new resolveApproval() closes the dialog / drops the queued entry when the call_id matches.
  • TUI (app.rs) — new dismiss_approval_prompt() clears the minibuffer approval prompt on the same signal, fixing the identical latent bug there.

Test

  • New daemon test tool_approval_resolved_is_not_persisted_to_transcript (broadcast-but-not-persisted).
  • Adding the SessionEvent variant was compiler-guided across all exhaustive matches (cli rendering/rain/scroll classifiers → hidden/none).
  • All affected suites pass: protocol 61, adapter-zarvis 149, agentd-cli 223, daemon all green; workspace builds clean.

Binaries touched

agentd (daemon broadcast + bundled web UI), agent (TUI), agentd-adapter-zarvis (emits the event).

Note

Verified the Rust side via tests; the browser dialog change itself wasn't exercised live in a browser (straightforward state/DOM update).

🤖 Generated with Claude Code

When a zarvis session showed a tool-approval prompt, every client that
mirrored it (the web approval dialog, the TUI minibuffer) had no way to
learn that the prompt was answered elsewhere — e.g. typed into the
session's PTY — so the dialog/minibuffer lingered after the decision.

Add a transient `SessionEvent::ToolApprovalResolved { call_id }`:

- The zarvis adapter (interactive + headless) emits it at every approval
  exit — approve, deny, auto-review, unsafe-auto, stop, interrupt — so
  it fires no matter how the prompt was answered.
- The daemon broadcasts it live but never persists it to the transcript,
  same as BrowserPreview / AgentStatus.
- The web UI closes its approval dialog (or drops the queued entry) when
  the resolved call_id matches.
- The TUI clears its minibuffer approval prompt on the same signal,
  fixing the identical latent bug there.

Adds a daemon test asserting the event is broadcast but not persisted.
@edwin-zvs
edwin-zvs merged commit 5c7e116 into main Jun 5, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the webui-approval-dialog-dismiss branch June 5, 2026 15:51
@edwin-zvs edwin-zvs mentioned this pull request Jun 6, 2026
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