Skip to content

fix(voice): fallback to binary check when pm is unavailable in Termux (#31015) - #31029

Closed
dskwe wants to merge 2 commits into
NousResearch:mainfrom
dskwe:fix/termux-voice-detection
Closed

fix(voice): fallback to binary check when pm is unavailable in Termux (#31015)#31029
dskwe wants to merge 2 commits into
NousResearch:mainfrom
dskwe:fix/termux-voice-detection

Conversation

@dskwe

@dskwe dskwe commented May 23, 2026

Copy link
Copy Markdown
Contributor

Problem

/voice on in Termux reports "Termux:API Android app is not installed" even when both the termux-api CLI package and the Termux:API Android app are installed and functional. Users confirm termux-microphone-record works from the command line.

Closes #31015

Root Cause

_termux_api_app_installed() relies exclusively on pm list packages com.termux.api to detect the companion Android app. In many Termux setups pm is either not on PATH or the Termux process lacks permission to invoke the Android package manager, causing the check to return False regardless of whether the app is installed.

Fix

Add a fallback in _termux_api_app_installed(): when pm fails (not found, permission denied, timeout, etc.), check whether termux-microphone-record binary exists via shutil.which(). The termux-api CLI package is a thin wrapper that only functions when the companion Android app is installed, so the binary's presence is a reliable proxy.

No behavioral change for environments where pm works correctly.

Design Rationale

The fix uses a simple two-step fallback: if pm list packages confirms the app, return True (back-compat). If pm fails for any reason (not on PATH, permission denied, timeout), fall back to checking whether termux-microphone-record is on PATH.

The key tradeoff vs a multi-probe approach: when pm returns clean but empty output (some devices do this due to permission restrictions even when the app is installed), a "definitive miss" branch would return False — same false-negative shape as the original #31015 bug. Our fallback avoids this by trusting the binary when pm is inconclusive.

The false-positive case (CLI installed without app) is rare — pkg install termux-api prompts for the companion app — and surfaces a precise runtime error from termux-microphone-record itself, which is more actionable than a pre-check denial.

Changes

  • tools/voice_mode.py_termux_api_app_installed() adds fallback to shutil.which("termux-microphone-record") when pm fails. Same public signature and return type.
  • tests/tools/test_voice_mode.py — 13 new tests in two classes:
    • TestTermuxApiAppInstalledFallback (8 tests): unit-level truth table for _termux_api_app_installed covering pm finds package, pm not found/permission denied/timeout/non-zero + binary present, pm fails + binary absent, non-Termux environment.
    • TestDetectAudioEnvironmentTermuxFallback (5 tests): end-to-end through detect_audio_environment confirming the misleading "app not installed" warning no longer fires when pm fails but the binary works.

How to Test

# New regression tests (13 tests)
python -m pytest tests/tools/test_voice_mode.py -k "Fallback or TermuxFallback" -v

# All voice mode tests
python -m pytest tests/tools/test_voice_mode.py -v

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have tool/tts Text-to-speech and transcription labels May 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing PR: #31028 also fixes #31015 with a more comprehensive graded detection ladder. Both modify tools/voice_mode.py.

@dskwe
dskwe force-pushed the fix/termux-voice-detection branch from c54884b to 68ce866 Compare May 23, 2026 17:18
@dskwe

dskwe commented May 23, 2026

Copy link
Copy Markdown
Contributor Author

Competing PR: #31028 also fixes #31015 with a more comprehensive graded detection ladder. Both modify tools/voice_mode.py.

Design rationale

The fix uses a simple two-step fallback: if pm list packages confirms the app, return True (back-compat). If pm fails for any reason (not on PATH, permission denied, timeout), fall back to checking whether termux-microphone-record is on PATH.

The key tradeoff vs a multi-probe approach: when pm returns clean but empty output (some devices do this due to permission restrictions even when the app is installed), a "definitive miss" branch would return False — same false-negative shape as the original #31015 bug. Our fallback avoids this by trusting the binary when pm is inconclusive.

The false-positive case (CLI installed without app) is rare — pkg install termux-api prompts for the companion app — and surfaces a precise runtime error from termux-microphone-record itself, which is more actionable than a pre-check denial.

dskwe added 2 commits May 26, 2026 00:33
_termux_api_app_installed() only checked via `pm list packages`, which
requires the Android package manager on PATH and the Termux process to
have permission to call it. If pm is missing or permission-denied (common
on newer Android / restricted Termux setups), the check returns False
even when Termux:API app is fully installed and functional.

Add a fallback: if pm fails for any reason, check whether
termux-microphone-record binary exists via shutil.which(). The CLI
package (termux-api) is a thin wrapper that only works when the
companion Android app is installed, so the binary's presence is a
reliable proxy.

Closes NousResearch#31015.
…ousResearch#31015)

Two new test classes covering the _termux_api_app_installed fallback:

TestTermuxApiAppInstalledFallback (8 tests):
  - pm finds package → True (back-compat happy path)
  - pm not found / permission denied / timeout / non-zero + binary present → True
  - pm not found / timeout + binary absent → False
  - non-Termux environment → False

TestDetectAudioEnvironmentTermuxFallback (5 tests):
  - End-to-end through detect_audio_environment confirming the
    misleading 'app not installed' warning no longer fires when
    pm fails but the binary is on PATH.
  - pm failure modes: not found, permission denied, timeout, non-zero.
  - pm fails + no binary still blocks voice mode.
@dskwe
dskwe force-pushed the fix/termux-voice-detection branch from 68ce866 to 2aaf5b8 Compare May 25, 2026 16:52

@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 isolating the Termux false-negative; current main still has the single pm gate at tools/voice_mode.py:68-82, so the reported failure remains actionable.

Problems

  • tools/voice_mode.py:91 falls back to shutil.which() after a clean pm invocation that simply did not list com.termux.api, not only after an unavailable or failed pm. That bypasses the Android-app gate introduced in commit c3141429b, including the runtime gate at current-main tools/voice_mode.py:370.
  • The new tests do not cover the clean-miss-plus-binary case. Current main explicitly preserves the no-app block in tests/tools/test_voice_mode.py:309-323.

Suggested changes

  • Distinguish a failed/non-zero pm probe from a clean package miss; use the binary fallback only for the former, or add a stronger independent probe before treating the latter as available.
  • Add direct and environment-level coverage for a zero-exit empty pm result with the binary present.

Automated hermes-sweeper review.

Comment thread tools/voice_mode.py
# the companion Android app is installed. The pm-based check can
# fail when the binary is not on PATH or the Termux process lacks
# permission to invoke the Android package manager.
return shutil.which("termux-microphone-record") is not None

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.

This fallback also runs when pm completed successfully but did not list the app. That bypasses main's explicit CLI-without-Android-app block; limit the fallback to an exception/non-zero probe result (or add a separate authoritative probe), and add a clean-miss-plus-binary regression test.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 13, 2026
@teknium1

Copy link
Copy Markdown
Contributor

The Termux:API detection fix landed in #73520 via your/#31028's probe-ladder version (this PR was its dupe). Thanks!

(Landed via #73520, merge e04c2a9ebd.) Closing.

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

Labels

P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users 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.

[Setup]: /voice on doesn't work in Termux even with Termux:API installed

3 participants