fix(voice-mode): robust Termux:API detection so /voice on works when pm probe is unreliable - #31028
fix(voice-mode): robust Termux:API detection so /voice on works when pm probe is unreliable#31028xxxigm wants to merge 2 commits into
Conversation
…ilures \`pm list packages com.termux.api\` is the canonical way to detect the Termux:API Android app, but on some devices it gives a false negative even when the app is installed and \`termux-microphone-record\` runs fine — the symptom reported in NousResearch#31015 (\`/voice on\` complaining "Termux:API Android app is not installed"). Replace the single probe with a graded strategy: 1. Try \`pm list packages com.termux.api\` (current behaviour). 2. If \`pm\` isn't on PATH or returns non-zero, fall back to \`cmd package list packages com.termux.api\` — the modern Android API 28+ equivalent that's present on devices where \`pm\` is gone. 3. If both probes are inconclusive (binary missing, permission denied, timeout, or non-zero exit) and \`termux-microphone-record\` is on PATH, trust the binary. The CLI ships in the \`termux-api\` package which is essentially only useful with the Android app installed; users who installed it deliberately almost always have the app too. Polarity matters: a false negative on this gate blocks \`/voice on\` entirely (the user-reported symptom), while a false positive only surfaces a precise runtime error from the binary itself when it tries to talk to the missing app — strictly more actionable. The clean-probe-but-no-package case still returns False, so the existing "Termux:API CLI installed without the app" warning still fires when the package manager *can* tell us the app is missing. Refs: NousResearch#31015
…h#31015 fallback Two test classes lock in the new detection contract: 1. TestTermuxApiAppInstalledProbeLadder — drives _termux_api_app_installed with a fake subprocess.run dispatcher and walks each rung of the ladder: - non-Termux env returns False (no probes run), - pm-confirms returns True (back-compat), - pm-clean-miss → cmd-confirms returns True, - pm-FileNotFoundError → cmd-confirms returns True, - pm-TimeoutExpired → cmd-confirms returns True, - pm-nonzero-exit → cmd-confirms returns True, - both probes inconclusive + binary on PATH returns True (the core NousResearch#31015 case), - both probes inconclusive + no binary returns False, - both probes clean-miss returns False (the genuine "CLI without app" case keeps the existing warning), - case-insensitive package match for ROMs that capitalise differently. 2. TestDetectAudioEnvironmentTermuxFallback — end-to-end through detect_audio_environment, asserting the misleading "Termux:API Android app is not installed" warning no longer fires when probes are inconclusive but the binary is on PATH (the user-reported NousResearch#31015 symptom), AND that the warning still fires when probes can conclusively report the app is missing. Refs: NousResearch#31015
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused fallback ladder and the regression coverage. The current-main single-pm check still has the reported false-negative path at tools/voice_mode.py:68-82, so this is worth salvaging.
Problems
tools/voice_mode.py:128falls back to the microphone binary whenever any probe was inconclusive. For example, a clean emptypmresult followed by missing/nonzerocmdreturnsTrue, even though the docstring attools/voice_mode.py:98-101says one clean miss is definitive. The new test attests/tools/test_termux_api_detection.py:183-200covers only two clean misses, not the mixed result.
Suggested changes
- Record a clean-miss state separately; keep probing for a positive result, then reject if any clean miss occurred and no probe confirmed the package before using the binary fallback.
- Add the mixed clean-miss/inconclusive regression case through
_termux_api_app_installed()anddetect_audio_environment(). - Preserve current main's
stdin=subprocess.DEVNULLhardening attools/voice_mode.py:78when resolving the stale hunk.
Automated hermes-sweeper review.
| if "package:com.termux.api" in (result.stdout or "").lower(): | ||
| return True | ||
|
|
||
| if inconclusive and shutil.which("termux-microphone-record") is not None: |
There was a problem hiding this comment.
This fallback also runs when an earlier probe completed successfully with no package match and a later probe was inconclusive. That contradicts the documented definitive-miss rule: track a clean-miss state and return False after all probes if no probe confirmed the package. Please add a regression for clean empty pm + unavailable/nonzero cmd + binary present.
|
Merged into main via consolidated salvage PR #73520 (merge Your contribution is credited to you in git history. Thank you! Closing this PR as merged-via-salvage. |
What does this PR do?
Fixes #31015. `/voice on` was rejecting working Termux setups with the misleading "Termux:API Android app is not installed" warning even when both `termux-api` (CLI) and the Termux:API Android app were installed and `termux-microphone-record` worked fine. The cause was a single brittle probe (`pm list packages com.termux.api`) that returns a false negative on some Android versions / Termux configurations — `pm` may not be on PATH, may return non-zero for the calling user, may time out, or may return empty output even when the package is present.
Replace the single probe with a graded ladder in `tools/voice_mode.py`:
Also normalises the package match to be case-insensitive (defensive against ROMs that capitalise the prefix differently).
Related Issue
Fixes #31015
Type of Change
Changes Made
Backwards compatible: probe behaviour for the happy path (`pm` works) is identical, no config keys, no schema changes, no platform-specific calls outside Termux.
How to Test
```bash
New regression tests (12 tests, ~0.3s)
./scripts/run_tests.sh tests/tools/test_termux_api_detection.py
Combined sweep — all voice mode tests still pass
./scripts/run_tests.sh tests/tools/test_voice_mode.py \
tests/tools/test_termux_api_detection.py
expected: 73 passed (61 existing voice_mode + 12 new), 1 pre-existing
failure on test_voice_mode.py::TestSubprocessTimeoutKill::test_timeout_kills_process
unrelated to this PR — confirmed unchanged on upstream/main with the
same live-system-guard error from tests/conftest.py:596.
```
End-to-end behaviour on the user's Termux device after the fix:
```
$ /voice on
[before #31015 fix:]
Voice mode unavailable in this environment:
Termux:API Android app is not installed.
Install/update the Termux:API app to use termux-microphone-record.
[after #31015 fix, same device:]
Voice mode active. Press Space to talk.
```
If a user genuinely has the CLI installed without the app, `pm list packages` returns cleanly with no match and the warning still fires unchanged. If both `pm` and `cmd package` are unavailable on a stripped-down ROM and the user truly doesn't have the app, the binary attempt at runtime surfaces a precise error from `termux-microphone-record` itself (e.g. broadcast-not-delivered) which is more actionable than a static pre-check.
Checklist