Skip to content

fix(cli): disarm continuous voice on hotkey during STT/agent (#67545) - #67569

Closed
Enough1122 wants to merge 1 commit into
NousResearch:mainfrom
Enough1122:fix/67545-voice-continuous-hotkey-disarm
Closed

fix(cli): disarm continuous voice on hotkey during STT/agent (#67545)#67569
Enough1122 wants to merge 1 commit into
NousResearch:mainfrom
Enough1122:fix/67545-voice-continuous-hotkey-disarm

Conversation

@Enough1122

@Enough1122 Enough1122 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #67545.

Narrow fix for the two specific non-recording paths the bug report calls out: Ctrl+B during STT (_voice_processing=True) or agent-busy (_agent_running=True) was a silent no-op — continuous stayed armed and the loop auto-restarted after the turn. The only workaround was /voice off.

This PR does not introduce a full hotkey-toggle semantic. It only inserts a _voice_continuous = False disarm in the two existing early-return paths the issue identifies, leaving the other non-recording paths unchanged. See "Scope vs. #67550 / #67573" below for the deliberate boundary.

What changed

cli.py::handle_voice_record, else branch, before the existing guards:

if cli_ref._agent_running:
    with cli_ref._voice_lock:
        cli_ref._voice_continuous = False
    return
...
if cli_ref._voice_processing:
    with cli_ref._voice_lock:
        cli_ref._voice_continuous = False
    return

Existing behavior on the recording branch (if cli_ref._voice_recording) is unchanged — that path already disarms continuous today and continues to do so.

Why narrow on purpose

The issue reporter asked specifically for the two paths they hit:

  • Pressing Ctrl+B while the agent is mid-turn and not yet back at the prompt → expect continuous to drop.
  • Pressing Ctrl+B while the previous take is still being transcribed → expect continuous to drop.

A broader "Ctrl+B is a global toggle" interpretation (#67550, #67573) changes semantics on paths the issue does not call out — e.g. TTS playback, interactive clarify/sudo/approval/slash_confirm prompts — and risks surprising users who currently rely on those paths being inert. That's a policy decision the maintainers should make, not one I want to bake in via the narrowest possible fix.

Scope vs. #67550 / #67573

PR Approach Covers STT? Covers agent? Covers TTS / interactive prompts?
#67569 (this) Per-guard disarm in the two specific paths the issue names ✗ (intentionally)
#67550 Global disarm at the top of the else branch (toggle semantic) ✓ (proposed)
#67573 Global disarm at the top of the else branch (toggle semantic) ✓ (proposed)

Happy to close in favor of #67550 or #67573 if maintainers prefer the broader toggle. The diff in this PR is a strict subset of either. If they prefer the narrow fix, the diff here is the smallest correct change.

Test plan

  • tests/hermes_cli/test_voice_wrapper.py — 44/44 pass locally.
  • The fix is bounded to two early-return paths in handle_voice_record. The disarm is guarded by cli_ref._voice_lock to match the existing lock discipline around _voice_continuous writes elsewhere in the same handler.

Branch: fix/67545-voice-continuous-hotkey-disarm @ 7976bd96.

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

Thanks for the focused CLI fix. The current-main premise is confirmed: cli.py:13697 and cli.py:13704 return from the non-recording hotkey path without clearing _voice_continuous, while cli.py:14937 restarts recording after a turn when that flag remains armed. The added locked assignments address that state transition without changing the recording branch.

Problems

  • The PR changes only cli.py; it adds no regression coverage for the classic CLI binding. tests/hermes_cli/test_voice_wrapper.py:682 tests the separate hermes_cli.voice API, and tests/tools/test_voice_cli_integration.py:711 currently uses source/AST checks rather than executing handle_voice_record.

Suggested changes

  • Add a runtime test for Ctrl+B/configured record-key handling with _voice_continuous=True in both _agent_running and _voice_processing states, asserting the flag is cleared and recording is not started.

Automated hermes-sweeper review.

Comment thread cli.py
# transcribed, pressing the hotkey means "disarm continuous"
# rather than "start a new take". Without this, Ctrl+B during
# STT/agent is a silent no-op and the loop auto-restarts
# after the turn — the only way out is /voice off (#67545).
if cli_ref._agent_running:

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.

Please add a runtime regression test for both new disarm branches. The existing voice-wrapper test exercises hermes_cli.voice.stop_continuous(), not this classic CLI keybinding, so it would not catch a future regression here.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 19, 2026
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard tool/tts Text-to-speech and transcription needs-decision Awaiting maintainer decision before any implementation labels Jul 19, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to #67550 and #67573. The competing patches differ in the exact non-recording paths that disarm continuous mode.

@Enough1122

Copy link
Copy Markdown
Contributor Author

cc @alt-glitch — re: the competing #67550 / #67573 note. Updated the PR body to make the boundary explicit. Summary:

#67569 (this) is intentionally the narrowest possible fix. It only touches the two early-return paths the issue reporter actually hit:

  • _agent_running == True → disarm continuous, return
  • _voice_processing == True → disarm continuous, return

No global hotkey-toggle semantic. #67550 and #67573 both propose putting a disarm check at the top of the else branch, which would also change behavior on:

  • TTS playback
  • _clarify_state / _sudo_state / _approval_state / _slash_confirm_state

Those paths are not in the scope of #67545, and changing them is a policy call (some users may rely on Ctrl+B being inert during a confirmation prompt so they don't accidentally drop continuous mode mid-confirmation). I didn't want to make that call as part of a "fix the reported bug" PR.

Recommendation:

  1. If maintainers prefer the global toggle semantics → close this in favor of fix(cli): disarm continuous voice on hotkey press during STT/agent (#67545) #67550 or fix(cli): Ctrl+B voice hotkey disarms continuous during STT/agent #67573. The diff here is a strict subset of either; no merge conflicts expected.
  2. If maintainers prefer the narrow fix → keep this one open. fix(cli): disarm continuous voice on hotkey press during STT/agent (#67545) #67550 and fix(cli): Ctrl+B voice hotkey disarms continuous during STT/agent #67573 should then close (or re-scope to just the issue's two paths, which is what this PR already does).

I've added a "Scope vs. #67550 / #67573" comparison table to the PR body so the choice is obvious from the description without needing to read the other two diffs.

Branch fix/67545-voice-continuous-hotkey-disarm is unchanged — only the PR body moved.

— written by Hermes Agent on behalf of @Enough1122

…earch#67545)

- Add locked _voice_continuous = False assignments in both guard branches
  of handle_voice_record: when _agent_running is True and when
  _voice_processing is True. Previously the hotkey was a silent no-op in
  these states and the loop auto-restarted after the turn.
- Add runtime regression tests that extract the real handle_voice_record
  closure from cli.py via AST and exec it against a real CLI instance,
  testing both disarm branches and a negative control (review feedback
  on NousResearch#67569).
@Enough1122
Enough1122 force-pushed the fix/67545-voice-continuous-hotkey-disarm branch from 7976bd9 to a09b6e0 Compare July 24, 2026 11:29
@Enough1122

Copy link
Copy Markdown
Contributor Author

Review feedback addressed — a09b6e0a7 adds 3 runtime tests in tests/tools/test_voice_cli_integration.py:

  • test_disarms_when_agent_running
  • test_disarms_when_voice_processing
  • test_does_not_disarm_when_no_guard_active (negative control)

The handler is extracted from cli.py AST and exec'd against a real CLI instance built via _make_voice_cli, so the assertions fail if either of the locked _voice_continuous = False assignments in #67569 is reverted. — written by Hermes Agent on behalf of @Enough1122

@Enough1122

Copy link
Copy Markdown
Contributor Author

Closing this stale PR for now: the current patch still has unresolved review/test-scope gaps, and it is unlikely to be merged in its present form. Reopen or submit a focused follow-up if the issue remains relevant.

@Enough1122 Enough1122 closed this Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades tool/tts Text-to-speech and transcription type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: CLI continuous voice — record hotkey (Ctrl+B) ignored during STT/agent; only /voice off exits loop

3 participants