Skip to content

[fix] Let only the session on screen answer the push-to-talk chord [AGE-4239] - #6477

Merged
ashrafchowdury merged 1 commit into
release/v0.114.7from
fix/push-to-talk-hidden-sessions-6452
Sep 3, 2026
Merged

[fix] Let only the session on screen answer the push-to-talk chord [AGE-4239]#6477
ashrafchowdury merged 1 commit into
release/v0.114.7from
fix/push-to-talk-hidden-sessions-6452

Conversation

@ashrafchowdury

Copy link
Copy Markdown
Contributor

Context

Holding the push-to-talk chord (⌃⌥ / Ctrl+Alt) in the chat composer captured nothing, and the composer stayed in its recording state after the message was sent.

Three separate causes, all on the dictation path (voice-message mode is still gated off behind VOICE_MESSAGE_MODE_ENABLED, so the shortcut only ever drives dictation):

  1. AgentChatPanel keeps every visited session mounted behind display: none, and usePushToTalk binds its listener on the document. One chord armed every mounted mic at once. Each opened its own SpeechRecognition, they fought over the single microphone, and the composer in view was as likely to lose as to win. That reads as dictation hearing nothing.
  2. The composer's read-only lock followed the recogniser's active flag, which trails stop() by hundreds of milliseconds to seconds while the browser closes its speech socket. Releasing the chord left the editor locked across that whole teardown.
  3. Sending mid-dictation never told the recogniser. It stayed lit until it was stopped by hand, and every word spoken after the send was discarded in silence.

Changes

Only the session on screen answers the chord. usePushToTalk takes the mic's own root and checks isOnScreen before it arms, then rechecks when the arm delay elapses so switching sessions mid-hold does not open the mic behind the switch. isOnScreen is the helper the repo already uses for exactly this hazard (ApprovalCard guards its window-level key handler the same way).

The editor lock follows intent, not the recogniser. onDictatingChange now reports transcribe.recording, which flips the moment the chord is released. The editor's own dictation session still follows active, so the recogniser's trailing final result still lands where it belongs.

A send closes the mic. The mic leaves its stopper on useVoiceComposer, the same shared voice surface it already reports dictation errors and the editor lock through, and both composers call endDictation() as they submit. The recogniser itself stays inside the button on purpose: lifting it would re-render the whole composer on every interim word.

A send also retires the editor's dictation session. submitEditorAsMarkdown empties the root but left dictationRef alive, and the session recreates its nodes on demand. A trailing result arriving after the send therefore wrote the sent text straight back into the empty composer. clear() gets the same treatment for the same reason.

Everything except the two composer call sites lives in @agenta/chat and @agenta/ui, so desktop and /m are fixed together.

Tests

Six new unit tests, each confirmed to fail with its fix reverted:

  • voiceInputButtonPushToTalk.test.tsx: a hidden session ignores the chord; one mic opens when a hidden session is mounted alongside a visible one; the composer unlocks on release without waiting out the teardown; the mic hands the composer a working stopper.
  • usePushToTalk.test.ts: an off-screen session never arms, and a session switched away during the arm delay does not open the mic.
  • richChatInputDictationSend.render.test.tsx: a trailing result after a send (and after clear()) cannot refill the composer.

Suites: @agenta/ui 176/176, @agenta/chat 570 passing. The 8 failures in tests/unit/state/sessionMessages.test.ts (localStorage undefined) predate this branch and are untouched by it.

Verified against the running desktop app with a stubbed recogniser:

  • With 3 sessions mounted and 2 hidden, one chord opened 1 recogniser. Revealing the hidden panes made the same chord open 3, which confirms the on-screen guard is the only thing suppressing them.
  • Dictated text landed, the editor was locked during dictation, and contenteditable was back to true 150ms after release with onend still 3 seconds away.
  • Sending while dictation was still running: the mic went from "Stop voice input" back to "Hold ⌃⌥ to dictate", stop() was called once, the composer emptied, and it stayed empty through the trailing result and the late onend.

What to QA

  • Open two or more agent sessions in the playground so both tabs stay mounted. Hold ⌃⌥ in one and speak. The words land in the session you are looking at, and no microphone error appears on the other tabs.
  • Hold ⌃⌥, dictate, release. The composer is editable straight away. You do not have to wait before you can edit or send.
  • Dictate and press Send without stopping the mic. The message sends, the mic returns to its idle state on its own, and the composer stays empty.
  • Regression: click the mic button to start and stop dictation. It still latches on the first press and stops on the second.
  • Regression: dictate during agent onboarding (before the agent exists) and press Enter. The agent is created and the mic closes.

Fixes #6452

… chord

Holding Ctrl+Alt captured nothing, and the composer stayed in its dictation
state after the message was sent.

The chat panel keeps every visited session mounted behind display:none, and
usePushToTalk binds its listener on the document, so one chord armed every
mounted mic at once. Each opened its own SpeechRecognition, they fought over
the single microphone, and the composer in view was as likely to lose as to
win — which reads as dictation hearing nothing. The guard is the one the repo
already uses for this, isOnScreen, checked when the chord arms and again when
the arm delay elapses, so switching sessions mid-hold does not open the mic
behind the switch either.

The composer's lock now follows intent rather than the recogniser. The browser
takes hundreds of milliseconds to seconds to close a session after stop(), and
holding the editor read-only across that teardown blocks the edit-and-send the
person released the chord to do. The editor's own dictation session still
follows `active`, so the trailing final result still lands.

A send now closes the mic. Sending mid-dictation left the recogniser running
with nowhere to write — it stayed lit until it was stopped by hand, and every
word after the send was discarded in silence. The mic leaves its stopper on the
shared voice surface, the same seam it already reports dictation errors and the
editor lock through, and both composers end dictation as they submit. Lifting
the recogniser itself would re-render the whole composer on every interim word,
which is why it stays in the button.

A send also retires the editor's dictation session. The recogniser flushes a
last result on the way out, which arrives after the composer has been emptied,
and the session recreates its nodes on demand — so the words that just left as
a message were written straight back into the empty box. clear() gets the same
treatment for the same reason.

Voice-message mode stays gated off; this is the dictation path the shortcut
actually drives.

Fixes #6452
@linear-code

linear-code Bot commented Sep 2, 2026

Copy link
Copy Markdown

AGE-4239

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agenta-documentation Ready Ready Preview Sep 2, 2026 5:51am UTC

Request Review

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📘 Docs preview

Status ✅ Ready
Preview https://pr-6477-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit a2ed931aeb8d9a5e899562c3398b7ee528a595e9

This comment updates in place on every push.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Website preview

Preview URL: https://pr-6477-agenta-website-preview.mahmoud-637.workers.dev

Built from a2ed931aeb8d9a5e899562c3398b7ee528a595e9. This comment updates in place on every push.

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Summary

Summary by CodeRabbit

  • New Features

    • Added automated production monitoring for documentation pages, sitemap responses, and crawler access.
    • Documentation routes now serve directly from the production docs site.
    • Added canonical redirects to consistently use HTTPS and the non-www domain.
  • Bug Fixes

    • Sending or clearing a message now reliably ends active voice dictation.
    • Push-to-talk no longer activates microphones in hidden chat sessions.
    • Sitemap links and page canonical URLs now consistently use the expected format.
  • Tests

    • Added coverage for voice input, dictation cleanup, redirects, and sitemap validation.

Walkthrough

The PR adds Cloudflare canonical redirects, direct docs-worker routing, sitemap and production monitoring checks, and canonical build validation. It also updates chat dictation teardown, visible push-to-talk handling, composer submission, and related unit tests.

Changes

Website delivery and docs monitoring

Layer / File(s) Summary
Canonical redirect configuration and deployment
.github/workflows/16-website-production.yml, website/scripts/configure-canonical-redirect.mjs, website/scripts/configure-canonical-redirect.test.mjs, website/scripts/verify-canonical-redirects.sh, website/AGENTS.md
The deployment creates or updates a Cloudflare 308 redirect rule and verifies HTTP, www, path, query, and asset redirects.
Canonical sitemap and build validation
website/astro.config.mjs, website/scripts/verify-build.mjs
Sitemap URLs omit trailing slashes except for the root. Build verification checks generated pages and matching canonical links.
Docs routing and production monitoring
docs/wrangler.production.jsonc, docs/README.md, docs/scripts/monitor-production.mjs, .github/workflows/20-docs-monitor.yml
Docs routes point directly to agenta-docs. A scheduled workflow checks sitemap and representative pages for browser, Googlebot, and Bingbot responses.

Voice dictation lifecycle

Layer / File(s) Summary
Voice stop contract and control state
web/packages/agenta-chat/src/hooks/useVoiceComposer.ts, web/packages/agenta-chat/src/components/VoiceInputButton.tsx
The voice hook exposes a stopper. The button assigns the recognizer stop function to that ref and reports recording state.
Visible push-to-talk control
web/packages/agenta-chat/src/hooks/usePushToTalk.ts, web/packages/agenta-chat/src/components/VoiceInputButton.tsx
Push-to-talk checks whether its root is visible before arming and before starting recognition.
Composer submission and dictation teardown
web/mobile/src/features/chat/Composer.tsx, web/oss/src/components/AgentChatSlice/components/AgentComposerDock.tsx, web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx
Composers stop dictation before submission. RichChatInput retires dictation before send and clear operations.
Voice lifecycle validation
web/packages/agenta-chat/tests/unit/components/voiceInputButtonPushToTalk.test.tsx, web/packages/agenta-chat/tests/unit/hooks/usePushToTalk.test.ts, web/packages/agenta-ui/tests/unit/richChatInputDictationSend.render.test.tsx
Tests cover hidden sessions, delayed arming, recognizer teardown, stopRef behavior, and trailing recognition results.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to a2ed9

The PR adds production edge redirect configuration that depends on correctly scoped Cloudflare permissions and can leave the prior redirect state unrecovered if a later deployment or verification step fails. It is mergeable with explicit owner confirmation of token scope and rollback handling.

Sequence Diagram(s)

sequenceDiagram
  participant ProductionWorkflow
  participant configureCanonicalRedirect
  participant Cloudflare
  participant verifyCanonicalRedirects
  ProductionWorkflow->>configureCanonicalRedirect: configure zone redirect
  configureCanonicalRedirect->>Cloudflare: update canonical ruleset
  ProductionWorkflow->>verifyCanonicalRedirects: verify redirect responses
  verifyCanonicalRedirects->>Cloudflare: request canonical URLs
Loading
sequenceDiagram
  participant Composer
  participant VoiceInputButton
  participant SpeechRecognition
  participant RichChatInput
  Composer->>VoiceInputButton: end dictation
  VoiceInputButton->>SpeechRecognition: stop recognition
  Composer->>RichChatInput: submit or clear
  RichChatInput->>RichChatInput: retire dictation session
Loading
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changes fix shortcut-driven dictation and close the microphone after sending, but they do not implement or verify the linked issue's stated voice-message audio capture and sending behavior. The PR… Clarify that #6452 concerns dictation rather than voice-message audio, or implement and test shortcut-driven voice-message capture and sending in addition to the current dictation fixes.
Out of Scope Changes check ⚠️ Warning The website canonical-redirect scripts, sitemap changes, production workflow changes, documentation-monitor workflow, and related documentation are unrelated to the linked chat dictation issue. [#6452 Remove the website deployment, sitemap, canonical-redirect, and documentation-monitor changes from this pull request, or link them to a separate issue with matching objectives.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 15 files. (5 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: restricting the push-to-talk chord to the session currently on screen. The issue identifier and fix prefix do not obscure the meaning.
Description check ✅ Passed The description directly explains the push-to-talk, dictation, microphone cleanup, editor-lock, and trailing-result fixes covered by the changeset.
Full details: Linked Issues check

Explanation

The changes fix shortcut-driven dictation and close the microphone after sending, but they do not implement or verify the linked issue's stated voice-message audio capture and sending behavior. The PR description states that voice-message mode remains disabled. [#6452]

Full details: Out of Scope Changes check

Explanation

The website canonical-redirect scripts, sitemap changes, production workflow changes, documentation-monitor workflow, and related documentation are unrelated to the linked chat dictation issue. [#6452]

Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 15 files. (5 skipped: 5 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/push-to-talk-hidden-sessions-6452

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Team

Run ID: 0c4c9b08-9b6d-4801-93ea-74b65aa3946e

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5e720 and a2ed931.

📒 Files selected for processing (20)
  • .github/workflows/16-website-production.yml
  • .github/workflows/20-docs-monitor.yml
  • docs/README.md
  • docs/scripts/monitor-production.mjs
  • docs/wrangler.production.jsonc
  • web/mobile/src/features/chat/Composer.tsx
  • web/oss/src/components/AgentChatSlice/components/AgentComposerDock.tsx
  • web/packages/agenta-chat/src/components/VoiceInputButton.tsx
  • web/packages/agenta-chat/src/hooks/usePushToTalk.ts
  • web/packages/agenta-chat/src/hooks/useVoiceComposer.ts
  • web/packages/agenta-chat/tests/unit/components/voiceInputButtonPushToTalk.test.tsx
  • web/packages/agenta-chat/tests/unit/hooks/usePushToTalk.test.ts
  • web/packages/agenta-ui/src/RichChatInput/RichChatInput.tsx
  • web/packages/agenta-ui/tests/unit/richChatInputDictationSend.render.test.tsx
  • website/AGENTS.md
  • website/astro.config.mjs
  • website/scripts/configure-canonical-redirect.mjs
  • website/scripts/configure-canonical-redirect.test.mjs
  • website/scripts/verify-build.mjs
  • website/scripts/verify-canonical-redirects.sh

Included review availability: Your plan provides up to 8 included reviews per hour; 4 remain after this review.

Comment thread .github/workflows/16-website-production.yml
@ashrafchowdury ashrafchowdury reopened this Sep 2, 2026
@ashrafchowdury
ashrafchowdury changed the base branch from release/v0.114.5 to main September 2, 2026 14:33
@ashrafchowdury
ashrafchowdury changed the base branch from main to release/v0.114.7 September 3, 2026 06:01
@ashrafchowdury
ashrafchowdury merged commit 3ec5cb0 into release/v0.114.7 Sep 3, 2026
98 of 103 checks passed
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.

(bug) Voice message not captured when using keyboard shortcut; recording UI doesn't close after sending

1 participant