# v2.49.0 — owner-only private files, a Hands-free settings page, and fixes for silent failures

**Released 2026-11-05.** Upgrade:

```sh
sudo snap refresh yazses     # snap
pipx upgrade yazses          # or: uv tool upgrade yazses
scoop update yazses          # Windows
```

Restart the daemon afterwards (`yazses restart`). Almost everything below corrects something that did not do
what it said; the two changes you can see are on the Settings window.

## What you will notice

- **A Hands-free page in Settings.** The hands-free controls live on their own page, in two columns when the
  window has room. It adds no new capability: the controls are the ones that already existed.
- **A checkbox for Persian text normalisation** on the Speech page. It is off until you tick it.
- **Settings explains itself.** The Speech page says why the Language box is greyed, the Features search hint
  names its filters in words, and the Updates page says what "Check" does.
- **The tray recovers on its own.** After an error state it keeps polling and returns to idle when the daemon
  is back, and it no longer shows "idle" when the daemon never started.
- **Clearer failures.** A speech device that will not start now names the device and the cause, not a missing
  model. A wrong model name, a 404, a timeout or a rate limit is no longer blamed on the GPU. An unreadable
  `config.toml` no longer prints an empty `[]:` section.

## Security

- **Private files are created owner-only.** Meeting transcripts, clipboard history, scratch notes, the learning
  corpus, voiceprints, few-shot examples and `daemon.log` (with its rotated backups) now get mode `0600`.
  Files created by an earlier version are tightened the next time YazSes writes to them, not all at once.
- **The owner-only helpers no longer follow symbolic links.** A symlink planted where a private file belongs
  cannot redirect a write elsewhere. **Behaviour change:** if one of those files is a symlink, YazSes now
  refuses to write it and says so, instead of writing through the link.

## Fixes

- **Meetings.** A meeting whose microphone disappears, or opens but never delivers audio, is reported as
  stalled instead of recording silence, and a meeting whose input device vanished ends at its length cap.
- **Dictation.** A microphone that disappears mid-hold is no longer filed as a silent room. The disfluency
  filter no longer deletes repeated spoken digits. The phonetic corrector no longer rewrites paths, URLs,
  e-mail addresses, file names or versions, and no longer takes seconds on a very long unbroken word. Streaming
  partials are taken back when a held or staged burst is consumed without typing.
- **Daemon lifecycle.** `yazses start` and `restart` stop only your own real YazSes daemons and never delete a
  lock a live daemon holds. With `XDG_DATA_HOME` set, the lock and pid file are found where the daemon put them.
  ydotool injection releases all keys even when typing fails.
- **AI-agent microphone (MCP).** `ask_human` can no longer exceed its hourly budget when several agents ask at
  once, and the `[mcp] ask_human_per_hour` limit is enforced across calls. The MCP stdio server survives a
  deeply nested JSON line or a non-object `params`, and the IPC server survives a deeply nested request.
- **Commands.** `yazses hotkey show` prints an unreadable-config warning once, not three times, and
  `yazses corpus destroy` says what it deletes.
- **Biosignal (still off by default, still never run on real hardware).** Hostile or corrupt recordings fail
  with a clear error, and the false-activation upper bound is the exact Poisson limit.
- **Documentation.** Latency figures are marked as single-run and decode-only, EMG is no longer described as a
  working accessibility feature, gaze is no longer described as shipped, and the packaging channel table was
  re-checked against v2.48.1. Design pages no longer publish a cut-off meta description.

Also in the repository, not in the product: research notes, a model-manifest validator, and a Proposed
ADR on whether to recommend Parakeet where it loads. None of them changes what the software does.

What was checked: the full test suite (including a run with the FreeBSD dependency set), ruff, mypy and the
documentation build on the release commit, and every CI leg on `main` before this release was cut.

What was not checked: the Settings window and the tray on a real desktop session, Windows and macOS dictation on
real machines, Wayland typing, Android, screen readers, real MCP clients, and any biosignal device. Two known
gaps remain: the Android Kotlin deduplication still collapses repeated digit pairs (the Python side is fixed),
and the Windows named pipe has no explicit security attributes.

The complete list is in the [changelog](https://github.com/MSKazemi/yazses/blob/main/CHANGELOG.md).
