Skip to content

v3.0.74 — Bridge-capability program (Phases 0–7)

Choose a tag to compare

@chandshy chandshy released this 05 Jun 01:41
· 6 commits to main since this release
5140345

Bridge-capability program — a subsystem-by-subsystem TDD redesign with a per-function multi-agent ultra-review to a 9.5 quality bar (8 phases, docs/quality-audit.md). Adds the gated empty_trash and mark_answered/mark_forwarded capabilities and fixes a fail-open folder-delete protection hole, an attachment-download OOM bypass, two search under-return bugs, the $Forwarded write/read asymmetry, the SMTP TLS divergence + IPv6 ::1 handling, and the multi-account keychain credential shadow.

Added

  • mark_answered / mark_forwarded tools — set/clear the IMAP \Answered flag and the $Forwarded keyword (the marker the forward tool writes and the readers now honour). Both thread sourceFolder and wrap the existing setFlag.
  • empty_trash tool — the one sanctioned permanent-delete path. PERMANENTLY EXPUNGEs the Trash mailbox (resolved by server \Trash special-use, so localised names like Papelera work) and only Trash — it can never be pointed at live mail. Gated by { confirmed: true } like the other destructive tools; short-circuits an already-empty Trash. Everywhere else, delete still means move-to-Trash. Scored 10/10 on the ultra-review.

Fixed

  • get_folders exposes specialUse + folderType so agents identify Trash/Sent/Archive by special-use (localised-account-safe) instead of English names, and avoid the \All union.
  • Fail-open folder protection closed (security): delete_folder/rename_folder could destroy a localised system mailbox if folder discovery threw; the protection check now fails closed.
  • getFolders robustness: one folder's STATUS failure no longer nukes the whole listing (Promise.allSettled); a cold-cache + disconnect throws instead of returning [].
  • getEmails pagination: an out-of-range offset returns an empty page instead of a clamped message #1.
  • Forwarded flag: reads $Forwarded/\Forwarded (the setter's flag) instead of the non-existent \Forward, which always read back as not-forwarded.
  • download_attachment memory safety: the oversize guard now checks the actual resolved byte length (the stale-metadata path could OOM); re-maps by filename if the re-fetch attachment order drifts.
  • bulk_copy failure messages now carry the source folder, matching bulk_move.
  • search_emails no longer returns [] on a connection failure (IMAP-012) — it throws, so a connection drop is distinguishable from "no matches".
  • search_emails hasAttachment no longer under-returns below the requested limit — the local attachment filter now runs before the limit (with a bounded over-fetch) instead of after; per-folder failures and folder-cap truncation are logged instead of silent; searchSingleFolder validates its folder defensively.
  • fts_search sinceEpoch is applied in SQL before LIMIT (was a post-LIMIT filter that could return far fewer than the limit).
  • Bridge SEARCH BODY/TEXT docs reconciledbody/text search criteria are wired and exposed (the limitation note was pre-Phase-0 stale).
  • SMTP now uses the shared buildBridgeTlsConfig (no more 4th inline TLS copy that could diverge from the IMAP path's pinned-cert/insecure-fallback contract), which also fixes SMTP + the IMAP IDLE socket not recognizing IPv6 loopback ::1 as localhost.
  • forward_email keeps the message in its thread — it now carries the original In-Reply-To/References (parity with reply_to_email), CRLF-strips the embedded original From/To in the quoted header, and logs (instead of silently swallowing) a $Forwarded/\Answered flag-set failure. Also fixed a latent crash: references is normalized to an array (mailparser returns a bare string for a single-reference message, which would have thrown on the send path).
  • Multi-account credential staleness fixed: the per-account keychain entry is now authoritative on startup, so a stale legacy keychain entry can no longer shadow the fresh per-account password a Settings save wrote (and account #2 can't inherit account #1's password). Single-account / config-plaintext installs are unaffected.
  • Background IDLE now runs for every account, not just the active one, so a non-active account still receives push cache invalidations instead of degrading to manual syncs.