Skip to content

feat(desktop): hold announcements while another app uses the microphone - #646

Draft
charleslpan wants to merge 2 commits into
mainfrom
charleslpan/daegu
Draft

charleslpan wants to merge 2 commits into
mainfrom
charleslpan/daegu

Conversation

@charleslpan

@charleslpan charleslpan commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds a native macOS helper (InputCapture.swift) that reports one boolean, whether any process is capturing from an input device, read from CoreAudio's running flag or a Bluetooth output's drop to its call codec, with no audio, device name, or process name.
  • A pure gate (call-quiet.ts) turns a foreign capture, Luke's own exchange excluded, into a hold after a short onset that releases a fixed grace after capture stops.
  • The hold pools with calendar meetings under the existing quiet flag, so the sleeping face, the announcer, and the same setting govern both; a released backlog now speaks its newest three rather than eight.
  • Updates AGENTS.md, PRIVACY.md, and the README to disclose the new read.

Evidence

  • Platform-independent checks: not run
  • macOS Electron verification (./scripts/verify.sh): not run

Automated visual evidence

Download the deterministic macOS evidence · workflow run

  • Commit: 4bd8d349becb7065bb704b4fdff2c07e925c1a86
  • Scenario: smoke
  • Physical-notch check: not performed by CI

Physical-device evidence

  • Screenshot or screen recording: not attached
  • Physical-notch check: not performed
  • Device/display configuration: not recorded

Notes

  • Blockers or follow-up verification: None

🤖 Generated with Claude Code

Open in Alchemize


Open workspace in Conductor

The only quiet Luke had was a calendar interval, which misses unscheduled
calls, calls that run over, and everyone with no calendar connected. macOS
already knows: CoreAudio says whether any process is capturing from an input
device, one boolean, no permission, no audio.

A new native helper reports that boolean and nothing else, watching every
input device rather than only the default, and reading a Bluetooth output's
drop to its call codec because a Bluetooth microphone does not set the
running flag while in use. A pure gate turns it into a hold: a foreign
capture — Luke's own exchange excluded — holds after a short onset and
releases a grace after it stops. The hold pools with the meetings under the
one flag the renderer already draws the sleeping face from, so the face, the
announcer, and their tests are unchanged, and the same setting governs both.
A released backlog now speaks its newest three rather than eight.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
luke-web Ready Ready Preview Sep 2, 2026 11:18pm UTC

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4bd8d34. Configure here.

this.#release = undefined;
this.#set(false);
}, CALL_QUIET_RELEASE_MS);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exchange releases hold during active calls

High Severity

#apply treats any live exchange as if capture stopped, so a hold already won by a foreign capture starts the release grace the moment the developer talks or types to Luke. After CALL_QUIET_RELEASE_MS the quiet lifts even though the microphone is still taken, and announcements can speak into the call until onset runs again.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bd8d34. Configure here.

return
}
let running = live.contains(where: isRunningSomewhere) || headsetOnCallCodec()
say("capture running=\(running ? 1 : 0)")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplex playback reported as microphone capture

High Severity

report treats kAudioDevicePropertyDeviceIsRunningSomewhere on every device that merely has input streams as a capture. That flag is device-wide, so playback on a USB headset, audio interface, or other duplex device counts as a call and holds announcements for the whole time audio is playing, plus the release grace.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 4bd8d34. Configure here.

Comment thread apps/desktop/native/macos/InputCapture.swift
The desktop caps released notices after filtering them by current status,
so the helper's own limit branch had no caller and duplicated the cap.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 47e590c9 Deployed Sep 2, 2026 by vercel[bot]
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.

1 participant