Skip to content

Releases

v0.3.9

Choose a tag to compare

@github-actions github-actions released this 10 Sep 13:52

Fixed

  • The readable-chat mode is now a standing choice, not a button that
    appears in one state only.
    Removing the last allowed chat correctly
    left an empty allowlist — agents able to read nothing — but the way back
    to "every chat" was a plain button at the foot of an empty list, easy to
    miss at exactly the moment it mattered. Both modes are now always on
    screen with the current one marked, so leaving the empty state never
    depends on finding a control inside it, and the warning banner names the
    way out rather than only the problem.

v0.3.8

Choose a tag to compare

@github-actions github-actions released this 10 Sep 13:33

Added

  • Limit which chats agents may read. Connecting an MCP client used to
    hand it every chat on the account. setup now asks what connected
    agents should see — every chat, only your own self-chat, or nothing
    until you say so — and the choice is editable afterwards from the
    dashboard's clients tab or the new scope command
    (--allow/--deny/--all/--list).

    Once a limit is on, chat-addressed reads outside it are refused and say
    the chat is out of scope rather than pretending it does not exist, so a
    model stops asking instead of retrying. Questions asked across all chats
    — global search, poll_new_messages, call history, contact search —
    come back filtered; contact search included, because an agent that can
    still enumerate your address book leaks exactly what the limit exists to
    withhold. Enforcement wraps the store once rather than at each call
    site, so a read tool added later cannot forget it.

    The limit is one setting for the whole server, not one per client: every
    client authenticates with the same bearer token, so the server cannot
    tell them apart. Only you can change it — no MCP tool writes
    config.json, the same rule the trust list already follows. And an
    empty allowlist means nothing is readable rather than everything,
    which is why the mode is stored separately from the list; check names
    that state, since every read tool then returning empty looks exactly
    like a broken install. The dashboard is deliberately exempt: it is your
    own window onto your own messages.

  • A clients tab in the dashboard. Every MCP client detected on this
    machine, whether it is installed, whether this server is added to it,
    and where its entry points — with one click to add or remove that entry.
    A config the doctor calls broken can now be fixed without dropping to a
    terminal. The broken flag repeats the doctor's own verdict rather than
    inventing a second opinion.

Fixed

  • Pairing a second number no longer shows the first number's data.
    Everything that belongs to a WhatsApp account — its messages, downloaded
    media, backups, trust list, readable-chat allowlist and pending
    scheduled sends — now lives under accounts/<number>/, and only the
    machine's own settings (rate limits, outbox roots, the HTTP token) stay
    shared. Switching numbers is clean in both directions: pair the first
    one back and its history and settings are exactly as they were.

    The worst case this closes was not the visible one. A pending scheduled
    send sat in a single shared file, so a message scheduled under one
    number would have fired from whichever number happened to be paired when
    its time came — a real message, to a real person, from an account they
    did not expect.

    Separation is by file rather than by a column every query has to
    remember: two accounts cannot see each other because their rows are not
    in the same database, including from code written long after this. The
    upgrade moves an existing install into the new shape by renaming files,
    never rewriting rows — deliberately, because adding an account column to
    the existing tables would mean rebuilding them, and that renumbers
    SQLite rowids, which here are both the full-text index's link to its
    content and the cursor poll_new_messages hands out. Renumbering them
    would have silently corrupted search results and made agents skip
    messages they never received.

Changed

  • trust and scope take a name or a phone number, not just a JID.
    Nobody knows their friend as 15551234567@s.whatsapp.net, and the old
    failure mode was silent: a mistyped JID was accepted, stored, and simply
    never matched anything. Both now accept a contact or group name, a phone
    number in any of the shapes people paste, or a JID. A name matching
    several people is never guessed at — the CLI lists the matches and asks,
    the dashboard shows them as a chooser, and nothing is written until one
    is picked. The same goes for the dashboard's trust and readable-chat
    boxes.

v0.3.7

Choose a tag to compare

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

Fixed

  • Reactions now sit on the message they react to, as a small pill on the
    bubble's lower edge, instead of floating as their own [reaction]
    bubbles — the way the real app shows them. One pill per person (a newer
    reaction from the same person replaces their older one, and an un-react
    removes it), with a count when several people used the same emoji.
  • Images opened from the chat pane now scale to fit the screen. The
    lightbox sized the image against a content-derived box, so a tall or
    large picture overflowed the viewport; it is now bounded to the viewport
    in both dimensions and always fits.

v0.3.6

Choose a tag to compare

@github-actions github-actions released this 25 Aug 07:06

Fixed

  • The chat pane now shows a chat's newest messages again. It paginated by
    database rowid (insertion order), which assumed rowid order matched time
    order — but a chat can hold a history-sync backfill whose rows were
    written long after the moments they record, and the LID fold in 0.3.4
    merged chats whose rows interleave in time. The result was an active
    chat opening on months-old backfilled messages instead of today's. The
    pane (initial load and in-place refresh) is now ordered by timestamp.
    No data was lost — only the view's ordering was wrong. The
    poll_new_messages MCP tool keeps its rowid cursor, which is correct
    for an agent reacting to newly ingested messages.

Added

  • Scroll up in a chat to load older messages — the pane fetches further
    back a page at a time and keeps your place as it grows, instead of only
    ever showing the newest ~50.
  • An "↑ older" control in the chat header asks your paired phone for
    messages from before the oldest one stored — the way to pull a chat's
    history in from scratch, one page at a time. It requests your own
    history from your own phone (it messages nobody) and is rate-limited by
    two cooldowns — one per chat, one global — so a held button or a script
    can't turn into the burst of history requests that risks a number being
    flagged; a cooldown shows as a short "try again in Ns" note, not an
    error.

v0.3.5

Choose a tag to compare

@github-actions github-actions released this 25 Aug 06:53

Added

  • Message search in the dashboard now works the way a chat app's does:
    results arrive as you type (no button), listed in the chat list's
    place with the chat's name, who said it, a snippet with the matches
    highlighted, and when. Clicking a result opens that chat parked on the
    message — scrolled to it and ringed — with a "latest" control to return
    to the live tail. With a chat open, results can be narrowed to it.
  • Pictures in the chat pane open full-size on click, brought forward over
    the blurred page; click outside, the close control, or Escape returns.

Fixed

  • The pair tab no longer shows the "Start pairing" button alongside the
    linked panel when this server is already paired (clicking it only ever
    produced "already paired").

v0.3.4

Choose a tag to compare

@github-actions github-actions released this 25 Aug 06:39

Fixed

  • One person is one chat again. WhatsApp increasingly addresses people by
    a privacy LID (...@lid) instead of their phone number, and the same
    contact could show up twice in the chat list — a phone-number chat with
    the history and a nameless LID chat with the newest messages — with the
    LID half reading as user …12345. Every LID the server can pair with a
    phone number (from the message itself, or from the pairing table
    WhatsApp syncs to the device) is now keyed on the phone number as it is
    stored: chats, senders, contacts, read receipts and calls alike. What was
    stored split by earlier versions is folded together on the next start,
    and again after every history sync, so pairings that arrive late still
    take effect. LIDs WhatsApp never pairs stay as they are.
  • History-sync stubs — the status lines WhatsApp keeps in a chat with no
    message behind them — no longer become empty [other] rows, or the
    phantom LID chats made of nothing else. Existing ones are dropped on the
    next start.

v0.3.3

Choose a tag to compare

@github-actions github-actions released this 25 Aug 06:17

Fixed

  • serve --http no longer gives up after one failed connect at startup.
    A service started at login often beat the network to it: with DNS not
    yet answering, the single connect attempt failed, and since WhatsApp's
    auto-reconnect only covers a connection that once existed, the process
    sat paired-but-offline until someone restarted it — with the dashboard
    reading "connecting" the entire time. The startup connect is now
    retried with a doubling backoff (5s up to a minute) until it lands, each
    failure logged with the wait before the next attempt, and a failed
    attempt now reports offline rather than a permanent connecting.

v0.3.2

Choose a tag to compare

@github-actions github-actions released this 24 Aug 14:12

Added

  • Refresh buttons on the dashboard's chat screen: one fetches only the
    messages that arrived since the pane was loaded (appending them in
    place, following the scroll only if you were already at the bottom)
    and one reloads the chat list. No background polling — updates load
    on demand.
  • Profile pictures in the dashboard's chat list, served through the
    server itself (the page still makes no external requests). Lookups are
    cached for a day — including the "no picture" answer — and live
    fetches are rate-capped, so rendering the list never turns into a
    burst of WhatsApp queries. The initial-letter avatar remains the
    fallback whenever no picture is available.
  • Media in the dashboard's chat screen: images and stickers preview
    inline (lazily, so only what scrolls into view is fetched), and every
    other attachment gets a download link. Files are served from the same
    per-chat media directory download_media uses, so each file is
    fetched from WhatsApp at most once, however many times it is viewed.
    Content types are verified server-side: only raster images ever
    render inline, and everything else — including SVG and HTML — comes
    down as an opaque attachment, so WhatsApp-supplied content can never
    run script on the dashboard's origin.

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 24 Aug 12:31

Fixed

  • The dashboard UI is no longer cached by the browser: after an upgrade,
    a previously opened dashboard could keep showing the old interface
    (e.g. the pairing controls while already paired) until a hard refresh.
    The Pair tab also renders the correct linked/unpaired panel instantly
    from the last known state, and "Paired." only announces a pairing you
    actually watched complete.

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 24 Aug 12:14

The personal-robustness release: the server heals itself after a
WhatsApp-side logout, Windows gets service support, messages get a backup
command — and a local web dashboard arrives, with QR pairing in the
browser, WhatsApp-style chat browsing, trust/schedule/draft management,
and one-click unlink.

Added

  • New logout command and an Unlink button on the dashboard's Pair tab:
    both sign this device out on WhatsApp's servers (the phone stops listing
    it under Linked devices), keeping local messages and settings, and reset
    to unpaired so a new QR pairing can start immediately. remove remains
    the local-only variant that never contacts WhatsApp.
  • Chats with no stored name now resolve a display name through the
    contacts table and lid mapping — in the dashboard, in list_chats/
    get_chat, and in send previews — and the dashboard falls back to a
    readable "+number" instead of a raw JID. Empty-bodied messages (system
    events, removed reactions) render as dim kind placeholders instead of
    blank bubbles, and a signed-out dashboard now says so in its header
    instead of freezing on the last live state.
  • The dashboard now shows designed signed-out and not-found pages that say
    how to get back in (run whatsapp-connect-mcp dashboard for a fresh
    login link), unknown API paths answer with a JSON 404, and a browser
    opening the bare server address is redirected to the dashboard.
  • The dashboard's chat window now looks and scrolls like WhatsApp Web's
    dark theme: independently scrolling chat list and message pane, message
    bubbles with in-bubble times, day dividers, and per-sender colors in
    groups. The whole dashboard adopts WhatsApp's dark palette, with
    loading skeletons and clearer action feedback throughout.
  • New backup command: writes a consistent snapshot of the message
    database to <data-dir>/backups/ (or --dest), safe to run while the
    server is up. Message history is the one thing re-pairing cannot
    recover.
  • service install|uninstall|restart now works on Windows, using a Task
    Scheduler logon task. The server runs as a minimized console window;
    closing it stops the server. Creating the task may require an
    Administrator terminal.
  • Connection-health diagnostics: dropped connections, keepalive timeouts,
    refused connects, temporary bans, and an outdated client now each print
    one clear line to the server log instead of passing silently.
  • Failed writes of incoming events to the message store are now counted
    and reported (previously silent).
  • check (and the doctor tool) gains an ingest finding that fails when
    incoming events are not reaching the message store, and the session
    finding now says when the server was logged out by WhatsApp mid-run.
  • status now shows connection state, last event time, reconnect count,
    and ingest-failure count.
  • A local web dashboard on the shared HTTP server: open it with the new
    dashboard command (prints a one-time login link). This first slice
    shows connection health, store counts, and doctor findings; pairing and
    management arrive in following changes. Localhost-only, protected by
    the same bearer token as the HTTP transport.
  • QR pairing from the dashboard: when the session is unpaired, the Pair
    tab shows the QR code in the browser — no terminal needed, including
    re-pairing after a WhatsApp-side logout.
  • Dashboard: read-only chat browsing and full-text search. Media messages
    show as placeholders; the dashboard never fetches media.
  • Dashboard: trust-list management (applies to a running server
    immediately), scheduled-send list/cancel, and a one-click backup.
  • Dashboard: pending send drafts can be reviewed, approved, or discarded.
    Approving delivers the exact content the agent drafted (rate-limited as
    always); a draft can be committed at most once no matter how the
    dashboard and the agent race.

Changed

  • serve --http now starts its listener before waiting for pairing, so
    the server is reachable while unpaired: store-backed read tools serve
    local data, and live/send tools report "no longer paired" until pairing
    completes. stdio serve still waits for pairing before starting.

Fixed

  • A WhatsApp-side logout no longer leaves a running server permanently
    broken: the server now says clearly that it was logged out, waits, and
    reconnects on its own as soon as the device is paired again — no restart
    needed. Tool calls while unpaired fail with "no longer paired — run
    setup" instead of a generic connection error.
  • A session-store failure while waiting for pairing (at startup, or after
    a logout) is no longer mistaken for a clean shutdown: it now prints a
    diagnostic and exits non-zero at startup, so a service manager restarts
    the server, and after a logout it says the re-check failed instead of
    going silent.
  • Dashboard: a failed pair start, trust change, schedule cancel, backup,
    or draft approve/discard now shows a message in that tab instead of
    failing silently.
  • Dashboard: starting pairing only accepts POST, matching every other
    mutating dashboard route.