fix(voice): fallback to binary check when pm is unavailable in Termux (#31015) - #31029
fix(voice): fallback to binary check when pm is unavailable in Termux (#31015)#31029dskwe wants to merge 2 commits into
Conversation
c54884b to
68ce866
Compare
Design rationaleThe fix uses a simple two-step fallback: if The key tradeoff vs a multi-probe approach: when The false-positive case (CLI installed without app) is rare — |
_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.
68ce866 to
2aaf5b8
Compare
teknium1
left a comment
There was a problem hiding this comment.
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:91falls back toshutil.which()after a cleanpminvocation that simply did not listcom.termux.api, not only after an unavailable or failedpm. That bypasses the Android-app gate introduced in commitc3141429b, including the runtime gate at current-maintools/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
pmprobe 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
pmresult with the binary present.
Automated hermes-sweeper review.
| # 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 |
There was a problem hiding this comment.
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.
Problem
/voice onin Termux reports "Termux:API Android app is not installed" even when both thetermux-apiCLI package and the Termux:API Android app are installed and functional. Users confirmtermux-microphone-recordworks from the command line.Closes #31015
Root Cause
_termux_api_app_installed()relies exclusively onpm list packages com.termux.apito detect the companion Android app. In many Termux setupspmis 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(): whenpmfails (not found, permission denied, timeout, etc.), check whethertermux-microphone-recordbinary exists viashutil.which(). Thetermux-apiCLI 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
pmworks correctly.Design Rationale
The fix uses a simple two-step fallback: if
pm list packagesconfirms the app, return True (back-compat). Ifpmfails for any reason (not on PATH, permission denied, timeout), fall back to checking whethertermux-microphone-recordis on PATH.The key tradeoff vs a multi-probe approach: when
pmreturns 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-apiprompts for the companion app — and surfaces a precise runtime error fromtermux-microphone-recorditself, which is more actionable than a pre-check denial.Changes
tools/voice_mode.py—_termux_api_app_installed()adds fallback toshutil.which("termux-microphone-record")whenpmfails. 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_installedcovering 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 throughdetect_audio_environmentconfirming the misleading "app not installed" warning no longer fires when pm fails but the binary works.How to Test