Skip to content

fix(web): intercept approval text input in chat - #2124

Merged
henrypark133 merged 3 commits into
stagingfrom
worktree-fix+approval-text-interception
Apr 7, 2026
Merged

henrypark133 merged 3 commits into
stagingfrom
worktree-fix+approval-text-interception

Conversation

@henrypark133

Copy link
Copy Markdown
Collaborator

Summary

  • When a tool requires approval in the web UI, typing "yes"/"no"/"always" (or aliases like "y", "n", "approve", "deny", "ok") in the chat input now resolves the approval card directly instead of sending a regular message
  • Previously, text input went through the backend message pipeline, causing duplicate approval prompts and "No pending approval for this thread" errors
  • The frontend intercepts keywords in sendMessage() and routes them through sendApprovalAction() — same code path as clicking the card buttons
  • Skips already-resolved cards (during the 1.5s removal animation), picks the most recent card when multiple are visible

Test plan

  • 7 new Playwright E2E tests in test_tool_approval.py:
    • test_text_yes_intercepts_approval — verifies "yes" resolves card, no user bubble, input cleared
    • test_text_no_intercepts_denial — verifies "no" shows "Denied"
    • test_text_always_intercepts_always — verifies "always" shows "Always approved"
    • test_text_aliases_intercepted — tests "y", "n", "approve", "deny"
    • test_text_approval_case_insensitive — tests "Yes", "YES", "No", "ALWAYS"
    • test_normal_text_not_intercepted_with_approval_card — regular text passes through normally
    • test_text_approval_resolves_real_tool_call — full E2E with real mock LLM tool call
  • All 12 approval tests pass (5 existing + 7 new)
  • cargo fmt clean, cargo clippy zero warnings

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 7, 2026 22:06
@github-actions github-actions Bot added scope: channel/web Web gateway channel scope: ci CI/CD workflows size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: core 20+ merged PRs labels Apr 7, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a 'DB MIGRATION' label for automated PR categorization and implements a new frontend feature to intercept chat keywords (such as 'yes', 'no', and 'always') for resolving tool call approvals. Comprehensive E2E tests were added to verify this interception logic. Feedback was provided to improve the robustness of the approval card selection in the frontend by ensuring the logic targets the most recent unresolved card, preventing potential issues during the resolution animation phase.

Comment thread src/channels/web/static/app.js Outdated
@henrypark133
henrypark133 force-pushed the worktree-fix+approval-text-interception branch from 3df5e54 to d5d32c8 Compare April 7, 2026 22:10
When a tool requires approval in the web UI, typing "yes", "no", or
"always" in the chat input now resolves the approval card directly
instead of sending a regular message. This prevents duplicate approval
prompts and "No pending approval" errors that occurred when text went
through the backend message pipeline.

The frontend intercepts approval keywords in sendMessage() and routes
them through sendApprovalAction() — the same code path as clicking
the Approve/Deny/Always buttons on the card.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the web chat UI to treat simple approval keywords typed into the chat input as direct approval/deny actions when an approval card is pending, avoiding backend-side duplicate approval handling. Also extends E2E coverage for this behavior and tweaks GitHub label automation to ensure a migration label exists and is applied based on changed paths.

Changes:

  • Intercept approval/deny keywords in sendMessage() and route them through sendApprovalAction() instead of posting a normal chat message.
  • Add Playwright E2E tests covering keyword interception and passthrough behavior.
  • Update PR label automation to create/apply a “DB MIGRATION” label based on migration-related file changes.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/channels/web/static/app.js Adds frontend keyword interception for pending approval cards before normal message sending.
tests/e2e/scenarios/test_tool_approval.py Adds new E2E tests validating text-based approval interception and non-interception for normal text.
.github/workflows/pr-label-scope.yml Ensures “DB MIGRATION” label exists, pins labeler action, and updates permissions.
.github/scripts/create-labels.sh Adds creation of the “DB MIGRATION” label in the label bootstrap script.
.github/labeler.yml Adds path-based mapping for “DB MIGRATION” label and updates header comments.
Comments suppressed due to low confidence (2)

src/channels/web/static/app.js:763

  • The interception logic only inspects the last .approval-card. If the last card was just resolved (it stays in the DOM for the 1.5s removal timeout) while an older card is still pending, keyword input won’t be intercepted and will instead be sent as a normal message. To match the intended behavior (“skip already-resolved cards”), walk the cards from newest→oldest and select the first card that is unresolved (and has a request id) before mapping keywords to sendApprovalAction().
    }
    if (lastAssistant) lastAssistant.setAttribute('data-streaming', 'true');

    // Mark turn as having received content so the Done safety net
    // does not trigger a spurious loadHistory() for streaming responses.

src/channels/web/static/app.js:771

  • The PR description lists aliases like y, n, approve, deny, ok, but the code also intercepts additional keywords (e.g., single-letter a, reject, cancel, yes always, approve always, etc.). Intercepting a in particular is very broad and will prevent users from sending a normal one-letter message whenever an approval card is visible. Either narrow the accepted keyword set to what’s intended, or update the PR description/tests to reflect and justify the expanded behavior.
    // Accumulate chunks and debounce rendering at 50ms intervals
    _streamBuffer += data.content;
    // Force flush when buffer exceeds 10K chars to prevent memory buildup
    if (_streamBuffer.length > 10000) {
      appendToLastAssistant(_streamBuffer);
      _streamBuffer = '';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/labeler.yml Outdated
@henrypark133
henrypark133 force-pushed the worktree-fix+approval-text-interception branch from d5d32c8 to b131ee6 Compare April 7, 2026 22:11
@henrypark133
henrypark133 requested a review from Copilot April 7, 2026 22:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/channels/web/static/app.js
Comment thread tests/e2e/scenarios/test_tool_approval.py
henrypark133 and others added 2 commits April 7, 2026 15:17
Address review feedback: instead of checking the last card and then
separately checking if it's resolved, find the most recent unresolved
card directly. Handles the edge case where the last card is resolved
(during 1.5s removal animation) but an earlier one isn't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses review feedback: adds a test where two approval cards are
visible, the newer one is resolved via button click, then typing "yes"
correctly targets the older unresolved card instead of falling through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 7, 2026 22:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/channels/web/static/app.js
@ironclaw-ci ironclaw-ci Bot mentioned this pull request Apr 10, 2026
@ironclaw-ci ironclaw-ci Bot mentioned this pull request Apr 18, 2026
theredspoon pushed a commit to theredspoon/ironclaw that referenced this pull request Jun 21, 2026
* fix(web): intercept approval text input ("yes"/"no"/"always") in chat

When a tool requires approval in the web UI, typing "yes", "no", or
"always" in the chat input now resolves the approval card directly
instead of sending a regular message. This prevents duplicate approval
prompts and "No pending approval" errors that occurred when text went
through the backend message pipeline.

The frontend intercepts approval keywords in sendMessage() and routes
them through sendApprovalAction() — the same code path as clicking
the Approve/Deny/Always buttons on the card.

[skip-regression-check]

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: find most recent unresolved approval card for text interception

Address review feedback: instead of checking the last card and then
separately checking if it's resolved, find the most recent unresolved
card directly. Handles the edge case where the last card is resolved
(during 1.5s removal animation) but an earlier one isn't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add E2E test for skip-resolved-card behavior

Addresses review feedback: adds a test where two approval cards are
visible, the newer one is resolved via button click, then typing "yes"
correctly targets the older unresolved card instead of falling through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: medium Business logic, config, or moderate-risk modules scope: channel/web Web gateway channel scope: ci CI/CD workflows size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants