Chad Piha bc306d27d4 feat(meetings): Auto-end forgotten meeting recordings (#1494)
* feat(meetings): Auto-end forgotten meeting recordings

Track reliable external microphone activity and show a protected
60-second countdown after meeting activity ends. Preserve recording
during reconnects, unreliable detection, and Keep recording actions.

* fix(meetings): Harden auto-end against stale binaries and races

* fix(meetings): Keep auto-end alive through coverage loss and CI races

Code review found that every major failure path killed the feature
silently. All fixes preserve the fail-safe property: degradation can
only delay or disable auto-end, never stop a live recording.

- Windows: a single unattributable session or flaky endpoint made the
  helper exit, with no respawn and a polling fallback that also lost
  meeting prompts. It now announces CAPABILITY AGGREGATE once and keeps
  emitting best-effort events; unattributable sessions refcount under
  pid 0, which the JS side can never exclude as its own, so they count
  as external capture and park the countdown.
- macOS: a process vanishing between enumeration and its property query
  permanently downgraded PID mode to aggregate, and the 5s heartbeat
  re-rolled that race for the whole session. Skip the vanished object
  and let the next reconcile retry it instead.
- Linux: one pactl source-output without application.process.id
  (echo-cancel, loopbacks) dropped PID reliability entirely. Skip
  module streams; client streams always carry the pid.
- CI: the notarize build raced the listener-publishing workflow and
  could cache a stale binary under a fresh source hash. The release tag
  is now pinned to MIC_LISTENER_VERSION in the C source on both ends,
  and CI downloads fail hard instead of silently falling back.
- Renderer: a rejected auto-end stop was swallowed; it is now reported
  through a required onError hook wired to the meeting logger.

* style(notes): Revert drive-by Prettier reflows

Restore ShareNoteDialog, OverviewNoteList, and useNoteDragAndDrop to
their merge-base content. The reflows were format-only, and the
ShareNoteDialog hunk conflicts with the policy-enforcement merge on
main for no benefit.

* fix(meetings): Exclude own audio helpers and persist auto-end stops

Auto-end never fired when system audio was captured: the capture
helpers (macOS audio tap, Linux portal, Windows loopback) are plain
child processes absent from app.getAppMetrics(), so the OS attributed
their capture to non-excluded pids and externalMicActive never went
false for the whole recording — verified empirically on macOS via the
mic listener reporting the tap helper's pid. Feed the helpers' live
pids into the detector's exclusion provider.

Transcript persistence was keyed to mounted views, and an auto-end
stop is the first stop that can fire while the notes view is closed:
the final tail since the last 30s flush was lost and diarization
results were dropped. Persist the final transcript in the store's stop
path after the main result arrives, move the periodic saver to the
always-mounted MeetingRecordingMount, and persist diarization results
session-scoped to the note that was recorded. NoteEditor's handler is
now display-only and gated to the recording note, so enrichment can no
longer land on whichever note happens to be open.

Toast when an auto-end request ends the live session — including when
a Keep click loses the race with the countdown — so the user learns
the recording ended instead of assuming it was kept. New
notes.meeting.autoEnded key in all 10 locales.

* feat(meetings): Confirm auto-end with audio silence and add a fallback

Mic ownership alone had two blind spots: an app that releases the mic
on mute could stop a live call while remote voices still played, and
the feature was inert wherever ownership is unreliable (older macOS,
coverage loss) or never armed (in-person meetings).

Combine the two signals in a tick-driven controller. In ownership mode
the countdown runs only while the system-audio channel is also quiet,
so remote speech defers it; the mic channel is deliberately ignored
there so room noise, typing, or talking to a colleague after the call
can't mask the end. When ownership is unreliable or no app ever held
the mic, both channels quiet for 60s prompts instead, tightened to 10s
when the last tracked meeting app exits.

Every raw meeting PCM chunk of both channels feeds a pure energy
monitor via a single hook in sendMeetingAudio, sharing the echo-leak
detector's thresholds through an alignment-safe RMS helper. Chunks are
ignored until the controller session exists, since system audio starts
streaming before the session is registered. Sleep gaps discard
accumulated silence instead of stopping on wake, and a mic hold shorter
than 30s no longer arms the mic-ignoring path on an in-person
recording.

Keep recording is now per episode with a 5-minute cooldown, the card
explains which signal fired, and Settings → Meetings gains an
"Auto-end forgotten recordings" toggle that takes effect immediately.

* fix(meetings): Make auto-end recording fail safe

* fix(meetings): Queue detections while a meeting recording session is live

The engine's user-recording flag is shared with dictation, so a dictation
that ends mid-meeting (or a cancel-hotkey press) clears it while the meeting
recording is still live. From then on a detection — most plausibly the
calendar reminder for a back-to-back meeting, which fires one minute before
its start, right inside the auto-end countdown — bypassed the queue, and its
prompt replaced the visible countdown card. The replacement path never
notifies the controller, so the countdown ran on unseen and stopped the
recording without any chance to keep it.

Gate detections on the engine's own recording session as well, which nothing
outside the recording lifecycle can reset: new detections are queued behind
it like the user-recording gate, and a post-dictation cooldown flush holds
the queue until the recording ends. This also stops the pre-existing "Take
notes?" prompt that could appear during the user's own manual recording after
a dictation.

* fix(meetings): Persist the final transcript before awaiting the main-side stop

Moving the final transcript save into the store's stop path (so auto-end and
owner-loss stops persist without the notes view mounted) also moved it after
`cleanup()` and the `meetingTranscriptionStop` round trip. The note editor
swaps from live segments to the stored transcript the moment `isRecording`
flips, so for the duration of that stop — up to a few seconds with a local
Parakeet flush — the tail since the last 30s periodic save disappeared from
the note and reappeared once the stop returned. On main the notes-view effect
wrote it in the same commit as the state flip.

Persist the captured segments before awaiting the stop; they are final once
the session is released. Only a segment-less recording still waits for
main's final transcript, as before. `persistFinalTranscriptAroundStop`
replaces `buildFinalMeetingTranscript`, whose only caller was this path.

* fix: clear meeting flag after recording session ends
2026-08-18 21:51:21 +02:00
2026-07-07 18:33:03 -07:00
2025-08-12 13:56:19 -07:00

OpenWhispr

OpenWhispr

License Platform GitHub release Downloads GitHub stars

The open-source and free alternative to WisprFlow and Granola.
Privacy-first voice-to-text dictation with AI agents, meeting transcription, and notes. Cross-platform for macOS, Windows, and Linux.

Website · Docs · Download · API · Changelog


OpenWhispr turns your voice into text, notes, and actions from your desktop. Press a hotkey, speak, and your words appear at your cursor. Choose between fully private offline transcription with local speech-to-text engines like Whisper and NVIDIA Parakeet — where your audio never leaves your device — or cloud processing for speed. No data collection, no telemetry, fully open source.

Download

Platform Download
macOS (Apple Silicon) .dmg
macOS (Intel) * .dmg
Windows .exe
Linux .AppImage / .deb / .rpm / .tar.gz

* On Intel Macs, live speaker identification and voice fingerprinting are unavailable: they depend on ONNX Runtime, which stopped shipping macOS x86_64 binaries in 1.24. Meetings still record and transcribe normally, and notes search falls back to keyword matching instead of semantic search.

Features

  • Voice dictation — global hotkey to dictate into any app with automatic pasting
  • Dictation translation — dedicated hotkey to dictate in one language and paste the text in another
  • AI agent — talk to GPT-5, Claude, Gemini, Groq, Tinfoil, OpenRouter, or local models with a named voice assistant
  • Voice agent hotkey — dedicated hotkey that sends your dictation straight to your AI agent as a command, no wake word needed and no cleanup pass; edit highlighted text in place, or opt in to sending a screenshot of your current screen as context
  • Meeting transcription — auto-detect Zoom, Teams, and FaceTime calls with live speaker diarization, voice fingerprinting, and Google, Microsoft, or Apple Calendar integration
  • Local speaker diarization — on-device speaker labelling with voice fingerprint recognition across meetings, no cloud required
  • Notes — create, organize, and search notes with folders, semantic search, cloud sync, and AI actions
  • Team spaces & sharing — free for signed-in users; share notes on the web with link, domain, or invite-only visibility, and collaborate in team spaces with roles, invitations, and server-enforced membership
  • Audio import — transcribe existing audio and video: drag in files, batch-upload, or paste a YouTube/audio URL, with optional speaker detection
  • Local or cloud — your choice — all core features (transcription, AI reasoning, speaker diarization, semantic search) work with local models or cloud providers — including GPU-accelerated local Whisper on Metal, CUDA, and Vulkan (AMD/Intel)
  • Enterprise controls — enforce organization policy, company SSO and SCIM, and centrally managed Amazon Bedrock or Azure OpenAI access without distributing cloud keys
  • Public API & MCP — manage notes and transcriptions programmatically or connect your AI assistant via the MCP server

Quick start

git clone https://github.com/OpenWhispr/openwhispr.git
cd openwhispr
npm install
npm run dev

Requires Node.js 24+. See the full documentation for setup guides, platform-specific instructions, and build details.

Documentation

Visit docs.openwhispr.com for:

Repo examples:

  • Custom ASR shim for Self-Hosted transcription against non-OpenAI-compatible ASR APIs

Tech stack

React 19, TypeScript, Tailwind CSS v4, Electron 41, better-sqlite3, whisper.cpp, sherpa-onnx, shadcn/ui

Star History

Star History Chart

Sponsors

Neon

Neon is the serverless Postgres platform powering OpenWhispr Cloud.

Contributing

We welcome contributions. Fork the repo, create a feature branch, and open a pull request. See the contributing guide for development setup and guidelines.

License

MIT — free for personal and commercial use.

Acknowledgments

  • OpenAI Whisper — speech recognition model powering local and cloud transcription
  • whisper.cpp — high-performance C++ implementation for local processing
  • NVIDIA Parakeet — fast multilingual ASR model
  • sherpa-onnx — cross-platform ONNX runtime for Parakeet inference
  • Hugging Face — model hub hosting Whisper, Parakeet, and embedding model weights
  • llama.cpp — local LLM inference for AI text processing
  • Electron — cross-platform desktop framework
  • React — UI component library
  • shadcn/ui — accessible components built on Radix primitives
  • Neon — serverless Postgres powering OpenWhispr Cloud
Languages
JavaScript 50.5%
TypeScript 44.2%
Swift 3%
C 1.1%
CSS 0.5%
Other 0.4%