Skip to content

fix: detect Zoom calls using non-default input devices#331

Open
calebchongc wants to merge 4 commits into
silverstein:mainfrom
calebchongc:fix/zoom-non-default-input-call-detection
Open

fix: detect Zoom calls using non-default input devices#331
calebchongc wants to merge 4 commits into
silverstein:mainfrom
calebchongc:fix/zoom-non-default-input-call-detection

Conversation

@calebchongc

Copy link
Copy Markdown
Contributor

Summary

Fixes #330.

This makes macOS native call detection work when Zoom is actively using a non-default input device, without broadening the generic mic gate in a way that would worsen #244.

Changes:

  • rebuild and bundle mic_check from mic_check.swift during Tauri builds;
  • update mic_check to expose CoreAudio active-input PIDs via --active-input-pids;
  • keep the all-input-device scan only as a helper fallback when process attribution is unavailable;
  • preserve PID/PPID/name in the detector's process snapshot;
  • require native call-app detection to match active input to the configured app's process family;
  • include Zoom meeting/helper processes such as ZoomHybridConf in Zoom's attributed process family.

Why this is not a #244 regression

#244 happens because generic mic activity can be caused by another app, such as a dictation tool, while an idle call app is merely running.

This PR avoids using broadened generic mic activity as the native app trigger when process attribution is available. The detector now checks whether the active input PID belongs to the configured native call app process family. A regression test covers idle zoom.us plus active superwhisper and expects no detection.

Tests

  • cargo test -p minutes-app -- --test-threads=1 — 211 passed
  • xcrun swiftc tauri/src-tauri/src/mic_check.swift -o /tmp/minutes-mic-check-test && /tmp/minutes-mic-check-test && /tmp/minutes-mic-check-test --active-input-pids
  • INSTALL_DIR=/Applications ./scripts/install-dev-app.sh --no-open
  • codesign --verify --strict --verbose=2 /Applications/Minutes\ Dev.app

Scope

This branch is intentionally limited to macOS call detection and the bundled mic_check helper. It does not include MLX, Sherpa ONNX, transcription backend, or dependency changes.

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the evil genius laboratory Team on Vercel.

A member of the Team first needs to authorize it.

@silverstein

Copy link
Copy Markdown
Owner

This is excellent work, Caleb. Filing #330 with a clean repro and then fixing it with CoreAudio active-input-PID attribution is exactly the right approach, and the descendant-expansion handling for Zoom helpers is thoughtful. I want to merge it. One blocker and two smaller things first.

Blocker: the attribution-unavailable fallback reopens #244.

In detect_active_call (call_detect.rs:766), mic_live is computed as:

let mic_live = active_input_pids.as_ref().map(|p| !p.is_empty()).unwrap_or_else(is_mic_in_use);

and the native-app loops fall back with:

let native_active = match (processes, active_input_pids) {
    (Some(processes), Some(active_input_pids)) => native_app_has_active_input(...),
    _ => native_app_matches_running_process(config_app, running),
};

When active_input_process_pids() returns None, and mic_check.swift returns exit 2 whenever CoreAudio process attribution is unavailable (older macOS, a permission/sandbox failure, or a missing helper), both lines collapse back to the pre-#244 behavior: generic mic-in-use plus 'zoom.us is running' attributes the call to Zoom. So an idle Zoom plus an active dictation tool re-triggers the exact false positive #330's fix is meant to prevent, on any machine where attribution is not available.

Suggested fix: once attribution is unavailable, native-app detection should not fall back to 'process exists plus generic mic active.' Either return no detection for native apps until PIDs are available (degraded, logged), or keep the all-device mic scan strictly as a liveness gate for the browser-tab probe path, never as the native trigger. Concretely, the _ => arms at lines 909 and 925 should not detect without Some(active_input_pids).

Two smaller items:

  • Test gap: idle_zoom_does_not_detect_when_another_app_has_input only covers active_input_pids = Some({superwhisper}). The path that actually reopens call_detection: false positives when dictation tools (superwhisper, etc.) are the active mic consumer #244 is active_input_pids = None, which is not tested. A case with idle zoom.us plus active superwhisper plus active_input_pids = None expecting no detection would have caught the above.
  • Helper scoping (minor): the Zoom helper names (ZoomHybridConf/CptHost/caphost/aomhost) are matched by exact name without requiring a zoom.us root or a descendant link, so a stale or identically-named helper holding the mic is accepted as Zoom. Rooting helpers in a matched zoom.us process (or bundle path) would tighten it.
  • CI is red only on a clippy too_many_arguments for detect_active_call_from_snapshot_with_processes (call_detect.rs:836), a one-liner once the rest settles (#[allow] or bundle the args into a struct).

Take whatever you have learned from the local crash reports into account too. Happy to talk through the fallback approach if useful. I think closing that one path is all this needs.

@calebchongc

Copy link
Copy Markdown
Contributor Author

I'll honestly attribute 100% of the credit for the fixes to codex and GPT, I'm just trying to use the app and throwing codex at any bugs I find while I'm at it to try and use the great framework you've built here 😂.

@silverstein

Copy link
Copy Markdown
Owner

Ha, all good. Credit can land wherever it likes. The only bar here is that the code is correct, and the review exists exactly so it doesn't matter whether a fix came from you, Codex, GPT, or a Ouija board. Funny enough the blocker above came out of a Codex adversarial pass on my end, so it was robots checking robots, and that is fine by me. You are finding real bugs in a tool you actually use and reporting them with clean repros, which is the most useful kind of contribution there is. Keep them coming.

@calebchongc

Copy link
Copy Markdown
Contributor Author

Added two follow-up commits to address the review feedback:

So I believe the blocker and the test gap from the review are now covered.

@silverstein

Copy link
Copy Markdown
Owner

This nails it. I verified the fix end to end:

  • The fail-open is gone: the native-app fallback is now _ => false instead of "process running + generic mic active," so when input attribution is unavailable, native detection simply does not fire. Fail-closed is the right call here (missing a detection beats a false one), and it pairs cleanly with the record-start permission gate that just landed on main.
  • The unrooted-helper case is tightened and has its own test (unrooted_zoom_helper_input_does_not_detect_zoom_call), and the active_input_pids = None regression test (idle_zoom_does_not_detect_when_active_input_attribution_is_unavailable) covers exactly the path that reopened call_detection: false positives when dictation tools (superwhisper, etc.) are the active mic consumer #244.
  • Clippy and the full test matrix are green.

One housekeeping thing before I merge: main moved a fair bit today (a couple of releases plus a microphone permission gate), and your branch now shows conflicts. The likely spot is build.rs, since your mic_check compile step overlaps with the CLI-sidecar build logic that landed in #324. Could you rebase on latest main and resolve? Once it is green again I will merge it. Thanks for turning this around so fast.

@calebchongc calebchongc force-pushed the fix/zoom-non-default-input-call-detection branch from b3b49d8 to d16acd2 Compare June 16, 2026 07:35
@calebchongc

Copy link
Copy Markdown
Contributor Author

Rebased this onto latest main and resolved the only conflict, which was the test-module overlap from the meeting-prompt fix already merged upstream.

Local checks are green:

  • cargo fmt --check
  • cargo clippy -p minutes-app --all-targets -- -D warnings
  • cargo test -p minutes-app (214 passed)

GitHub now reports the branch as mergeable. CI has restarted; Vercel still appears to be the external authorization gate.

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.

macOS call detection misses Zoom when Zoom uses a non-default input device

2 participants