Repository navigation
Releases: MaximeGaudin/void
Releases · MaximeGaudin/void
Release list
v0.15.2
Fixed
- Slack —
void reply --in-threadand scheduled replies (--at) to a message that is already inside a thread now post under the thread's parent. They used to pass the reply's owntsasthread_ts, which Slack's API says never to do. - Remote store —
--fileattachments keep their original name. They used to reach recipients as<uuid>-report.pdfbecause the staging UUID was part of the staged filename. - Remote store —
--fileuploads work for names with spaces or non-ASCII characters (my file.pdf,devis_signé.pdf). OpenSSH 9+scpkept the shell quotes as part of the remote path and failed withNo such file or directory.
v0.15.1
Changed
- Dependencies — updated
libheif-rsto 3.0 (optionalheicfeature),sha2to 0.11, and the minor/patch group (tokio, reqwest, serde_json, clap, uuid, rmcp, …).
Fixed
- Sync —
void --config <path> sync --daemon(and the setup wizard's "Start syncing now?") now uses<path>: the daemon loaded the default config and spawned its child without--config, so it failed or synced the wrong profile. - Gmail — the startup INBOX refresh renews the access token on
401and retries the message. A pass longer than the token's 1h lifetime (large inbox, rate-limited) used to skip every later message with401 Unauthorized. - Gmail —
void gmail draft createandvoid gmail draft updateaccept several attachments: repeat--file(--file a.pdf --file b.png). A second--fileused to be rejected by the argument parser. Remote mode stages every file.
Security
- Dependencies — bump
rustls0.23.46,rustls-webpki0.103.16,webpki-roots/webpki-root-certs1.0.11,quinn-proto0.11.19,ctutils0.4.6,tokio1.54.1 andevent-listener5.4.2 to fix RUSTSEC-2026-0338/0339/0340/0341/0342 (quinn-proto), RUSTSEC-2026-0346/0347/0348/0349 (rustls), RUSTSEC-2026-0381/0383 (distrusted emSign roots), RUSTSEC-2026-0382 (distrusted intermediates), RUSTSEC-2026-0385 (ctutils) and the unsound RUSTSEC-2026-0221 / RUSTSEC-2026-0386.cargo auditandcargo deny checkare clean.
v0.15.0
Added
- Pagination — paginated JSON output (
inbox,conversations,messages, …) now includespagination.has_more, so agents looping "fetch → process → fetch" can tell the default 50-item page is not the whole backlog without computing it fromtotal_pages. - Sync — configurable
retention_daysin[sync](default 15) prunes stored messages older than the cutoff, bounding database growth instead of keeping every message since the first sync forever. - Hooks —
void hook push [<name>...]copies local hook files to the remote host instore.mode = "remote", where the sync daemon actually runs them. - Doctor — new Hooks section: every configured hook with its last execution, flagged as an issue when an enabled hook stopped running while its trigger kept firing (new messages arriving, or a missed cron slot). In remote mode the hooks are listed on the remote host and executions read from the database snapshot.
- WhatsApp — optional
heicCargo feature (--features heic) transcodes outgoing.heic/.heifimages to JPEG (with thumbnail) via the systemlibheif, announcingimage/jpegonce transcoded. Off by default (LGPL opt-in, FFmpeg-style): default builds keep announcing the honestimage/heic/image/heifMIME from the 0.13.1 fix.
Fixed
- Gmail — inbox state now follows Gmail's thread model: a thread stays in the inbox while any of its messages carries
INBOX, and all its stored messages are unarchived with it. Previously, a thread put back in the inbox (un-archived in the web UI, or a new reply) could stayis_archivedin void because upserts never touchis_archivedon existing rows, and a message that gainedINBOXwithout being stored first (e.g. a sent message filed back into the inbox) was never ingested. A full INBOX reconciliation now also runs every hour, not only at startup or after an idle period. - Gmail — archiving a message now removes
INBOXfrom the older messages of its thread too, so the thread actually leaves Gmail's inbox. Messages newer than the archived one are left alone. - Gmail —
void gmail thread <id>accepts a message id: on a 404 it resolves the message's thread and retries, instead of failing. - Gmail —
void gmail draft updateno longer detaches a reply draft from its conversation. It now fetches the draft first and resends itsthreadId,In-Reply-ToandReferences, and re-attaches its existing attachments unless--fileis passed (which replaces them). New--reply-to <id>re-targets the draft to another message. - Gmail —
void gmail draft create --reply-tonow setsIn-Reply-To/Referencesfrom the original'sMessage-IDheader instead of the Gmail message id, so recipients' mail clients thread the reply. - Gmail —
void gmail searchandvoid gmail threadalways filldateas an RFC 3339 UTC timestamp derived from Gmail'sinternalDate(it wasnullfor messages served from the local store), and add atimestampfield matchingvoid inbox. - Remote store — read-after-write in
store.mode = "remote": a proxied write (archive,mute,send, …) now marks the local database snapshot stale, so an immediatevoid inboxre-fetches it instead of serving the pre-write copy for up todatabase_ttl_secs(archived items used to reappear). - Calendar —
void calendar respondaccepts the void event id (<connection>-<event>) as printed byvoid calendar, instead of forwarding it verbatim to Google (404).calendar updateandcalendar deletestrip the prefix too. The RSVP now updates your own entry on the guest list (the attendee Google flagsself, else your primary calendar's email) instead of the connection id: it no longer adds a bogus<email>-calendarattendee whose invite bounced. If you are not on the guest list, the command fails and leaves the event untouched. - Hooks (remote mode) — hooks defined on the client (
~/.config/void/hooks/) were silently ignored: the remote daemon only loads hooks from the server, sovoid hook listcame back empty and triage hooks never ran. Everyvoid hookcommand andvoid doctornow warn about ignored local hooks.void hook create --prompt-filenow uploads the prompt file to the server instead of reading the path there. - Hooks — the hooks directory now follows the config file in use (
<config dir>/hooks/, including with--config) instead of always the default config location, so the daemon and proxiedvoid hookcommands read the same hooks. - Update —
void updatenow detects a Homebrew-managed install (Cellarin the executable's path) and refuses to self-replace it, pointing tobrew upgrade voidinstead. Overwriting a Homebrew-installed binary in place would leavebrew's bookkeeping out of sync with the actual file, corrupting futurebrew upgrade/uninstall.void doctor's update note also suggests the right command for a Homebrew install.
v0.14.0
Added
- Slack — new
void slack download <message-id> --out <path>command to download file attachments, matching WhatsApp/Telegram/LinkedIn. Supports--file-indexto pick a single attachment; with multiple files and no index,--outis treated as a directory and every downloadable file is saved into it.
v0.13.1
Changed
- Doctor —
void doctornow always prints the local and (in remote mode) the remote host's version, instead of only when they differ from the latest GitHub release.
Fixed
- WhatsApp —
.heic/.heiffiles (the iPhone default) now classify as images instead of falling through toDocument, and announce their real MIME (image/heic/image/heif) rather than a falseimage/jpeg; PNG/GIF/WebP also announce their real MIME instead ofimage/jpeg. Nothing transcodes HEIC yet, so the previousimage/jpegclaim shipped JPEG-labelled HEIC bytes that receiving clients couldn't render.
v0.13.0
Added
- Circleback — new read-only connector for Circleback meeting notes and transcripts. Each meeting becomes a conversation carrying its notes, action items and (optionally) every transcript turn, searchable alongside messages. Configure with
api_key,backfill_days(default 365) andinclude_transcript(default true);void setuphas a wizard for it. - iMessage — new read-only connector for the macOS Messages store (iMessage and SMS), indexed into the same store and search index as other connectors. Requires Full Disk Access;
health_checkreports a clear message when it is missing. - iMessage —
void send/void replynow work, driving Messages.app over AppleScript and confirming delivery againstchat.db(is_from_me = 1) before reporting success, instead of trusting AppleScript's immediate (pre-delivery) acknowledgment. - Slack —
void mcpexposes anedittool forchat.update, alongside CLI support, reusing the same shared write service assend/reply. - Update —
void updatechecks GitHub for a newer release, downloads and sha256-verifies the matching platform binary, and installs it atomically (stopping and restarting the sync daemon if one is running).void remote updateruns the same thing on the SSH-configured remote host.void doctornow notes when a newer version is available.
Changed
- GitHub —
void setuplinks directly to https://github.com/settings/tokens when adding an account, to help create a PAT.
Fixed
- Slack — File sends (
void sendwith an attachment) no longer report success without server confirmation. The upload flow now types Slack's response and requires a non-emptyfilesarray before printing "Message sent", instead of trusting the upload ticket obtained before the share happened. - Gmail — Recipient display names with non-ASCII characters (e.g. "Maître") are now RFC 2047-encoded when composing an outgoing
To/Cc/Bccheader, matching what void already did forSubject. A raw UTF-8 name written straight into a header is only valid by convention; a strict mail client can reinterpret those bytes as Latin-1 and mangle the name (e.g. "Maître" rendered as "Maître").
v0.12.1
Fixed
- Security — Updated
rustlsto 0.23.45 andchacha20to 0.10.2, fixing RUSTSEC-2026-0285.
v0.12.0
Added
- Gmail —
void gmail searchandvoid gmail threadread from the local INBOX store when a usable body is already synced.--liveforces the Gmail API (in:sent, drafts, and unsynced mail still go to the network). - Gmail — Cross-process token bucket (~90 requests / 60s per account, stored in SQLite) so the CLI and sync daemon share quota instead of stampeding after a 429.
- Remote —
void remote statusreportslocal_versionandremote_versionso version skew between the client and the server binary is visible at a glance.
Fixed
- Slack — Retry HTTP 5xx and retryable JSON errors (
internal_error,fatal_error) on every GET/POST, including sends, with the sameRetry-Afterbackoff as 429. - Gmail — Retry transient API failures (429, 5xx, and 403
rateLimitExceeded) with exponential backoff, honouringRetry-Afterand the retry timestamp in Google's error body. - WhatsApp —
void send/void replyno longer report success on a dead or dying socket. Sends fail fast when the connection is down, and after the write a ping must round-trip before success is printed; if it does not, the command exits non-zero and says delivery is unknown. - WhatsApp — History sync after pairing is stored again. The library now delivers the backfill one conversation at a time, so the old bulk handler never ran and the pairing dump was dropped (observed: 775 conversations parsed, 4 rows stored). Progress is logged every 250 messages.
- Archive —
void archive <id>now dismisses the whole context group behind the item (Slack thread, Slack 1-hour channel group, Gmail thread) instead of a single row. The inbox shows one row per context, so archiving only the visible id let an older sibling resurface as the next representative. The response gainsarchived_count(rows newly archived by the call,0when it was already archived), and Gmail pushes the group in onebatchModifyrequest. - Remote — Proxied write commands print a warning on stderr when the server's
voidis a different version than the local client, instead of surfacing confusingunexpected argumenterrors from the older remote binary.
v0.11.1
Fixed
- WhatsApp — Sync no longer leaves the account looking permanently online. Void overrides
wa-rs's automaticavailablepresence withunavailableon connect, after push-name sync, after sends, and on a periodic refresh so contacts stop seeing you as always online and phone notifications keep working.
v0.11.0
Added
- GitHub — New read-only connector that syncs open PRs requesting your review, comments on your authored PRs, and @mentions via GitHub notifications. Each repository maps to a conversation so
void mute owner/repoworks at repo level. Configure viavoid setup. - Reddit — New connector that polls watched subreddits and surfaces posts matching your keywords and minimum score (one channel conversation per subreddit). Read-only mode uses application-only OAuth (
client_id+client_secret); enabling commenting duringvoid setupruns a browser OAuth flow, stores arefresh_token, syncs matching posts as comment threads, and lets you reply viavoid reply/void send --via reddit. Tune filters at runtime withvoid reddit subreddits|keywords|min-score|config. - Google News — New read-only connector that watches the public Google News RSS feed. Each configured keyword triggers its own search; matching articles land in your inbox, filtered by a recency window. Configure with
void gn keywords,void gn when,void gn language, andvoid gn country(or interactively viavoid setup). Language/country default tofr/FR; add one connection per edition to follow several. - MCP —
void mcpstdio server: named read/write tools (inbox,conversations,messages,search,contacts,channels,slack_saved,calendar,health,send,reply,forward,archive,mute) plus aruntool for full CLI parity via subprocess. See docs/mcp.md. - Gmail —
--cc/--bccon draft create/update,void send --via gmail,void reply,void forward, andvoid gmail forward(comma-separated). Headers are placed afterToand beforeSubject/MIME so Gmail honors them. - Gmail —
--signature/--signature-from <email>append the account HTML signature from Gmail send-as settings on all outgoing compose paths:void gmail draft create/draft update,void send --via gmail,void reply,void forward, andvoid gmail forward. Pass a body/comment without an existing signature (append is not idempotent). Forwards place the signature between the comment and the quoted message. First interactive use may open a browser to grantgmail.settings.basic(not requested at normal setup); non-interactive / MCP callers must grant that scope once via an interactive--signaturecommand (void setupre-auth does not). - Slack — Sync "Saved for Later" items from the Later view during background sync; view with
void slack saved. Setup wizard documents the requiredsearch:readscope. - Sync — Supervised mode auto-restarts connectors and the WhatsApp RPC server on failure with exponential backoff (5 s → 5 min, reset after 60 s stable, give up after 10 consecutive failures).
Changed
- Internal — Service layer (
crates/void-cli/src/service/) extracting read/write business logic shared by CLI commands and the MCP server. - Internal — Connector wiring uses a compile-time plugin registry (
inventory).ConnectorTypeis a string newtype; connection settings are a generic TOML table. Adding a connector no longer edits ~13 central files. Connection settings are validated at setup reload, sync start, andvoid doctor. Poll intervals are read from[sync]via the generic{id}_poll_interval_secskeys. No user-facing CLI behavior change. - MCP —
runresolves the first non-flag token as the subcommand, rejects global CLI flags inside args, and omits informational stderr on successful JSON/stdout-empty results. - Build — Bump minimum supported Rust version to 1.95 (required by sysinfo 0.39).
Fixed
- Gmail — Reject CR/LF and other ASCII control characters in
To/Cc/Bccso address fields cannot inject extra RFC 2822 headers (CLI and MCP compose paths). - WhatsApp — The daemon's RPC socket no longer disappears from disk, which broke every
void send --via whatsappwithNo such file or directory. A secondvoid syncused to unlink the running daemon's socket before discovering it could not take the lock; the sync lock is now acquired before the RPC endpoint is touched, a live endpoint is never replaced, and a watchdog rebinds the socket if it vanishes anyway (manualrm,/tmppruning). - WhatsApp — File attachments now work in notes-to-self sends.
- Sync — A lock file whose PID has been recycled by an unrelated process is now treated as stale instead of "another sync instance is running", so
void sync --restartstarts cleanly andvoid sync --stopcan no longer signal a stranger.--restartalso clears a lock that survives the stop. - Slack — Inaccessible saved items (left channels, deleted messages) are skipped instead of aborting the entire saved-sync pass.
- Inbox — Bulk archive (
archive --before) no longer archives backfilled messages before they appear in the inbox, and upsert no longer overwrites user archive decisions on re-sync. - Messages — Restore UTC midnight semantics for
void messages --since/--untildate filters during service-layer extraction (calendar date ranges remain local midnight).
Removed
- Google Drive — Removed the
void drivecommand andvoid-gdrivecrate. Drive file download was on-demand utility code outside void's inbox/sync model; use Gmail attachments or a dedicated Google Drive tool instead.