Skip to content

Releases: MaximeGaudin/void

v0.15.2

Choose a tag to compare

@github-actions github-actions released this 27 Oct 14:30

Fixed

  • Slack — void reply --in-thread and 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 own ts as thread_ts, which Slack's API says never to do.
  • Remote store — --file attachments keep their original name. They used to reach recipients as <uuid>-report.pdf because the staging UUID was part of the staged filename.
  • Remote store — --file uploads work for names with spaces or non-ASCII characters (my file.pdf, devis_signé.pdf). OpenSSH 9+ scp kept the shell quotes as part of the remote path and failed with No such file or directory.

v0.15.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 12:45

Changed

  • Dependencies — updated libheif-rs to 3.0 (optional heic feature), sha2 to 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 401 and retries the message. A pass longer than the token's 1h lifetime (large inbox, rate-limited) used to skip every later message with 401 Unauthorized.
  • Gmail — void gmail draft create and void gmail draft update accept several attachments: repeat --file (--file a.pdf --file b.png). A second --file used to be rejected by the argument parser. Remote mode stages every file.

Security

  • Dependencies — bump rustls 0.23.46, rustls-webpki 0.103.16, webpki-roots / webpki-root-certs 1.0.11, quinn-proto 0.11.19, ctutils 0.4.6, tokio 1.54.1 and event-listener 5.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 audit and cargo deny check are clean.

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 29 Sep 11:55

Added

  • Pagination — paginated JSON output (inbox, conversations, messages, …) now includes pagination.has_more, so agents looping "fetch → process → fetch" can tell the default 50-item page is not the whole backlog without computing it from total_pages.
  • Sync — configurable retention_days in [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 in store.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 heic Cargo feature (--features heic) transcodes outgoing .heic/.heif images to JPEG (with thumbnail) via the system libheif, announcing image/jpeg once transcoded. Off by default (LGPL opt-in, FFmpeg-style): default builds keep announcing the honest image/heic/image/heif MIME 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 stay is_archived in void because upserts never touch is_archived on existing rows, and a message that gained INBOX without 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 INBOX from 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 update no longer detaches a reply draft from its conversation. It now fetches the draft first and resends its threadId, In-Reply-To and References, and re-attaches its existing attachments unless --file is passed (which replaces them). New --reply-to <id> re-targets the draft to another message.
  • Gmail — void gmail draft create --reply-to now sets In-Reply-To / References from the original's Message-ID header instead of the Gmail message id, so recipients' mail clients thread the reply.
  • Gmail — void gmail search and void gmail thread always fill date as an RFC 3339 UTC timestamp derived from Gmail's internalDate (it was null for messages served from the local store), and add a timestamp field matching void 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 immediate void inbox re-fetches it instead of serving the pre-write copy for up to database_ttl_secs (archived items used to reappear).
  • Calendar — void calendar respond accepts the void event id (<connection>-<event>) as printed by void calendar, instead of forwarding it verbatim to Google (404). calendar update and calendar delete strip the prefix too. The RSVP now updates your own entry on the guest list (the attendee Google flags self, else your primary calendar's email) instead of the connection id: it no longer adds a bogus <email>-calendar attendee 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, so void hook list came back empty and triage hooks never ran. Every void hook command and void doctor now warn about ignored local hooks. void hook create --prompt-file now 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 proxied void hook commands read the same hooks.
  • Update — void update now detects a Homebrew-managed install (Cellar in the executable's path) and refuses to self-replace it, pointing to brew upgrade void instead. Overwriting a Homebrew-installed binary in place would leave brew's bookkeeping out of sync with the actual file, corrupting future brew upgrade/uninstall. void doctor's update note also suggests the right command for a Homebrew install.

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 14:46

Added

  • Slack — new void slack download <message-id> --out <path> command to download file attachments, matching WhatsApp/Telegram/LinkedIn. Supports --file-index to pick a single attachment; with multiple files and no index, --out is treated as a directory and every downloadable file is saved into it.

v0.13.1

Choose a tag to compare

@github-actions github-actions released this 17 Sep 12:09

Changed

  • Doctor — void doctor now 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/.heif files (the iPhone default) now classify as images instead of falling through to Document, and announce their real MIME (image/heic/image/heif) rather than a false image/jpeg; PNG/GIF/WebP also announce their real MIME instead of image/jpeg. Nothing transcodes HEIC yet, so the previous image/jpeg claim shipped JPEG-labelled HEIC bytes that receiving clients couldn't render.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 11:25

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) and include_transcript (default true); void setup has 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_check reports a clear message when it is missing.
  • iMessage — void send / void reply now work, driving Messages.app over AppleScript and confirming delivery against chat.db (is_from_me = 1) before reporting success, instead of trusting AppleScript's immediate (pre-delivery) acknowledgment.
  • Slack — void mcp exposes an edit tool for chat.update, alongside CLI support, reusing the same shared write service as send/reply.
  • Update — void update checks 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 update runs the same thing on the SSH-configured remote host. void doctor now notes when a newer version is available.

Changed

Fixed

  • Slack — File sends (void send with an attachment) no longer report success without server confirmation. The upload flow now types Slack's response and requires a non-empty files array 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/Bcc header, matching what void already did for Subject. 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

Choose a tag to compare

@github-actions github-actions released this 15 Sep 07:29

Fixed

  • Security — Updated rustls to 0.23.45 and chacha20 to 0.10.2, fixing RUSTSEC-2026-0285.

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 08:27

Added

  • Gmail — void gmail search and void gmail thread read from the local INBOX store when a usable body is already synced. --live forces 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 status reports local_version and remote_version so 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 same Retry-After backoff as 429.
  • Gmail — Retry transient API failures (429, 5xx, and 403 rateLimitExceeded) with exponential backoff, honouring Retry-After and the retry timestamp in Google's error body.
  • WhatsApp — void send / void reply no 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 gains archived_count (rows newly archived by the call, 0 when it was already archived), and Gmail pushes the group in one batchModify request.
  • Remote — Proxied write commands print a warning on stderr when the server's void is a different version than the local client, instead of surfacing confusing unexpected argument errors from the older remote binary.

v0.11.1

Choose a tag to compare

@github-actions github-actions released this 20 Aug 14:26

Fixed

  • WhatsApp — Sync no longer leaves the account looking permanently online. Void overrides wa-rs's automatic available presence with unavailable on 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

Choose a tag to compare

@github-actions github-actions released this 30 Jul 13:51

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/repo works at repo level. Configure via void 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 during void setup runs a browser OAuth flow, stores a refresh_token, syncs matching posts as comment threads, and lets you reply via void reply / void send --via reddit. Tune filters at runtime with void 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, and void gn country (or interactively via void setup). Language/country default to fr/FR; add one connection per edition to follow several.
  • MCP — void mcp stdio server: named read/write tools (inbox, conversations, messages, search, contacts, channels, slack_saved, calendar, health, send, reply, forward, archive, mute) plus a run tool for full CLI parity via subprocess. See docs/mcp.md.
  • Gmail — --cc / --bcc on draft create/update, void send --via gmail, void reply, void forward, and void gmail forward (comma-separated). Headers are placed after To and before Subject/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, and void 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 grant gmail.settings.basic (not requested at normal setup); non-interactive / MCP callers must grant that scope once via an interactive --signature command (void setup re-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 required search:read scope.
  • 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). ConnectorType is 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, and void doctor. Poll intervals are read from [sync] via the generic {id}_poll_interval_secs keys. No user-facing CLI behavior change.
  • MCP — run resolves 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 / Bcc so 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 whatsapp with No such file or directory. A second void sync used 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 (manual rm, /tmp pruning).
  • 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 --restart starts cleanly and void sync --stop can no longer signal a stranger. --restart also 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/--until date filters during service-layer extraction (calendar date ranges remain local midnight).

Removed

  • Google Drive — Removed the void drive command and void-gdrive crate. Drive file download was on-demand utility code outside void's inbox/sync model; use Gmail attachments or a dedicated Google Drive tool instead.