fix(desktop): preserve failed voice recordings - #72976
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tackling a real data-loss and error-redaction problem. Current main still unlinks the upload in finally and can return raw provider exception text from hermes_cli/web_server.py:4256-4264.
Problems
hermes_cli/web_server.py:4568makes the established no-speech result aprovider_errorresponse. The retained test attests/hermes_cli/test_web_server.py:3072-3103suppliessuccess=Falseplus an empty-transcript error and requires200with an empty transcript; this branch returns400and retains the recording instead.apps/desktop/src/app/contrib/hooks/use-session-tile-delegate.ts:75-83awaits profile resolution before callingpatchSessionTile. That helper persists only the currently active profile bucket (apps/desktop/src/store/session-states.ts:408-432), so a rail-profile switch during the await leaves the original tile without its owner backfill.
Suggested changes
- Preserve the current empty-transcript path before
provider_error, cleaning up any staged recovery record rather than surfacing a failure. - Persist the owner against the tile's originating bucket or guard the async completion against profile changes; cover the switch-during-resolution case.
This is an automated hermes-sweeper review.
4674e94 to
8a0e031
Compare
|
Updated to |
|
Vox Lockin lane 09 verification receipt:
|
Summary
Desktop currently deletes its uploaded recording in
finally, so a provider error, timeout, cancellation, or malformed response can destroy a long recording before the user has any recovery path. The raw provider exception can also cross the API boundary and expose credential-bearing details.This change makes Desktop STT failure-safe:
cleanup_pendingtombstones;hermes stt recovery list|retry|save|discardfor explicit recovery;Related: #37889, #37890, #53488, #66626.
Maintainer review follow-up
The latest revision addresses both automated review comments:
200with an empty transcript and remove the staged recording;The same review exposed adjacent equal-ID/profile races. The final revision therefore carries the authoritative owner through tab focus, native-window IPC/URL routing, resume/retry, branch/archive/delete, and live-runtime binding. Async resume completion uses an owner-aware compare-and-set, and the native-window registry uses an unambiguous tuple key. These paths have adversarial duplicate-ID, profile-switch, stale-completion, URL-parser, and delimiter-collision tests.
Lifecycle and privacy
The cache lives under
<HERMES_HOME>/.cache/stt-recovery/. POSIX directories/files are created as0700/0600; Windows uses the account's inherited ACLs. Manifests contain no transcript, raw exception, absolute path, or credential material. Expired entries are hidden immediately and pruned on the next cache access; there is no background service while Hermes is stopped.Recovery is enabled by default to prevent silent data loss. Operators can disable it or reduce its limits through
stt.recovery. This is an explicit privacy/reliability trade-off: physical expiry is access-triggered, while confidentiality is constrained by owner-only storage, opaque IDs, bounded retention/capacity, export/backup exclusion, and no remote list/read endpoint.A validated successful transcript commits deletion before the response is returned. This PR intentionally does not add a renderer acknowledgement protocol, so the narrow case where the connection drops after successful STT but before Desktop inserts the returned transcript remains documented follow-up scope.
Validation
ty,git diff --check, and the Windows-path production scan passed;Scope
This protects recordings submitted through Desktop/WebUI
/api/audio/transcribe. Inbound voice messages handled by messaging-platform adapters use a separate pipeline and are unchanged.