Memo v3.9.0 — Release Notes

Open Beta · August 22, 2026 · Download

The through-line of this release is making things actually correct instead of merely present. Web search went from "inject the user's raw message into every turn" to real tool-calling where the model decides per-message, at zero cost when it doesn't search. Reasoning-effort control went from static, occasionally-wrong per-vendor tables to live per-model capability discovery — after finding out the static version could return a hard 400 on a real request. And a security review of a live self-hosted Raspberry Pi deployment found and closed a genuine auth bypass (a Cloudflare Tunnel forwarding external traffic to loopback) plus three previously-known "High" issues that had sat open since the last audit.

Also landed: Memo can now be talked to from WhatsApp (message yourself, it replies) and from Telegram (connect a bot, message it, it replies) — and routines became something you can create, list, and cancel just by talking, with delivery targets hard-locked to you so the model can never pick an arbitrary recipient. A late-stretch live test surfaced the most consequential bug of the release: self-chat's agent tools were never actually reachable at all.

Rounding it out: a full LM-Studio-style redesign of the local dev gateway (now speaks both Anthropic- and OpenAI-compatible wire formats, with one-click Claude Code CLI connect), a system tray icon with minimize-to-tray, Dream's own configurable schedule, a token-spend breakdown by category in Stats, self-hosted multi-account per-capability permissions, the Kilo Code provider with a free/paid model browser, and a mobile navigation redesign.


Big Feature: Talk to Memo From WhatsApp

Message yourself on WhatsApp ("Message Yourself" chat) and Memo picks it up and replies — your own phone becomes another interface for your second brain. Opt-in (off by default), since it's the one WhatsApp feature that autonomously sends outgoing messages.

Landed in several live-tested rounds, each closing a real bug:

  • Core mechanism: incoming self-chat messages route through a dedicated background session — the same memory/intent/mood pipeline as normal chat — without touching whatever chat is open in the UI.
  • Root-cause bug caught live: WhatsApp's newer Linked-ID (@lid) addressing means a self-chat JID doesn't always arrive as the phone-number form the original matching logic checked. Fixed to match either form.
  • Native "typing…" indicator, refreshed periodically via WhatsApp's own presence API rather than faked with a placeholder message.
  • Stray status-marker text leaking into replies (a bare "9", "web_searchweb_search") — fixed by flipping the chunk filter so only genuine text passes through.
  • Slash commands: /new, /agent on|off, /web on|off, /status, /help — control Memo without opening the app.
  • Command replies were hardcoded Turkish, caught by a direct user question. Now follow the same Identity.UILanguage setting as the GUI, read fresh on every call.

Big Feature: Telegram Support — Same Thing, With a Bot

Connect a Telegram bot (Settings → Telegram, paste a token from @BotFather) and talk to Memo through it the same way. Deliberately narrower than WhatsApp: a bot can only see messages sent directly to it — no equivalent of full-account visibility without a much heavier phone-number login.

  • Security model built in from the start: whoever sends the bot its very first message is locked in as its permanent owner; every other account is silently ignored from then on — no reply, no leak.
  • Same command set, same background-session pattern, same typing indicator; replies follow Identity.UILanguage from day one.
  • No new dependency — a purpose-built long-polling Bot API client. The token is encrypted at rest with the same machine key providers.json uses.
  • A step-by-step "how to connect" walkthrough lives right in the Settings tab.

Big Feature: Routines From the Chat

Routines shouldn't require opening the Routines tab at all — typing "every day at 9am send me the latest AI news" into a WhatsApp or Telegram conversation just works. Three new agent tools: create_routine, list_routines, cancel_routine.

  • Hard security constraint decided up front: the model must never choose an arbitrary WhatsApp contact or chat as a delivery target. Delivery is always forced to the surface the routine was created from; another channel is added only if your text explicitly asks for it and it's actually connected — verified with unit tests passing a nil client/store.
  • Reversed after live testing: routines no longer tie tool auto-approval to a per-chat /auto-perm setting that might silently change before firing hours or days later. Chat-created routines that need agent mode always auto-approve their own safe tool calls, and every routine runs with agent mode and web search available regardless of global toggles.
  • Multi-channel delivery fixed: asking for a routine delivered to "Telegram and WhatsApp" was silently collapsed to one channel — now honored when the requested channel is genuinely connected.
  • /auto-perm on|off added to both surfaces: without it, an agent tool call needing permission inside self-chat had no way to actually ask — it silently timed out.

Fixed: Self-Chat's Agent Tools Were Never Actually Reachable

The most consequential bug of the release, caught only because a real WhatsApp transcript showed Memo answering as if create_routine simply didn't exist — generic hallucinated "I can't do that" text with zero capability awareness.

Root cause: self-chat's session is deliberately created as a background chat (so it never hijacks the UI), which never sets a project path — and that project path was exactly what decided whether agent tools were available. So tool access fell back entirely to the global agent toggle, off by default, with no way to turn it on from inside self-chat short of typing /agent on first. Every routine tool, web_search, and whatsapp_send had been genuinely unreachable this entire time.

Fixed by forcing agent availability unconditionally for self-chat. Its real safety boundary was always meant to be the permission-asking flow, not a second easy-to-forget gate sitting in front of it. Confirmed live afterward: a real request now genuinely runs a web search and returns current results.

Separately, web search's default flipped to on: with real tool-calling, the model decides per-turn whether to search at all, at zero cost on turns it doesn't. Existing installs are unaffected.

Big Feature: Web Search, Redesigned Twice Over

Web search started this stretch with a user-reported bug: blind search sent the user's entire raw message — filler words included — straight to DuckDuckGo as a literal query, returning near-random results.

  • First pass: an LLM call distilled the message into a 2–6 word query before searching.
  • Second, deeper pass: the real issue was design, not text processing. Blind mode ran a search on every single message whenever the toggle was on, whether or not the turn needed current information. Plain chat now uses the identical mechanism Agent Mode already had — real tool-calling scoped to just the web_search tool — so the model decides per-message, and the old always-on "searching..." status fires only when a search actually happens.
  • Bug caught during a doc pass, not a user report: the new path skipped Minimal Mode's zero-injection promise. Regression-tested.

Known, accepted gap: Orchestra Mode + web search + Agent Mode off has no tool-calling to plug into, so that combination now does no web search at all.

Big Feature: Reasoning Effort Control, Rebuilt on Live Discovery

Reasoning-effort ("low/medium/high", thinking budgets) was config-only — Memo never actually sent it. Getting it right took three passes:

  1. Per-provider effort levels, each vendor's actual request shape handled on its own terms — flat string for OpenAI/Grok/Groq/Ollama/llama.cpp, nested thinking config for Claude, numeric budget for Gemini, OpenRouter queried live since it varies by model.
  2. Found live and fixed: OpenCode Zen/Go were reusing OpenAI's static table on a false assumption — they're aggregators fronting many vendors behind one endpoint, with zero capability metadata published. The picker now hides itself there instead of guessing.
  3. Replaced entirely: researching real API surfaces turned up genuine per-model capability endpoints Memo wasn't using — and, worse, that sending OpenAI's reasoning_effort to a non-reasoning model returns a hard 400 Unsupported parameter, confirmed live against gpt-4o. Static tables are gone; Claude/Gemini/Ollama/OpenRouter query real capability data first, everything else shows nothing.

Extended to Agent Mode and Orchestra Mode, which the first pass had deliberately left out.

Big Feature: Dev Gateway, Redesigned and Extended

The local Anthropic-compatible gateway — point Claude Code at Memo via ANTHROPIC_BASE_URL — got a full pass:

  • Visual redesign around a docs-tree-style nav sidebar matching LM Studio's Developer screen.
  • New: OpenAI-compatible endpointPOST /v1/chat/completions and GET /v1/models through the exact same auth/routing/memory pipeline.
  • New: configurable system prompt applied to every gateway request, additive on top of what the calling tool sends.
  • New: one-click Claude Code CLI connect — writes ANTHROPIC_BASE_URL/key directly into the CLI's own settings file, prior values backed up and restored exactly on disconnect; plus a model-selection dropdown after Claude Code's own default model name turned out to get rejected.
  • Verified with a dedicated suite that the gateway injects nothing beyond what's explicitly configured — captured outbound requests prove byte-for-byte passthrough under default config.

Self-Hosted Multi-Account Gets Per-Capability Permissions

v3.5.5 gave self-hosted Memo admin/user accounts, but "user" was one fixed bundle. Now an admin can grant access one switch at a time — Models, Memory, Agent, Calendar, WhatsApp, Telegram, Routines — shown as checkboxes at account creation, editable afterward.

  • Fail-closed by design: a brand-new user account with nothing checked can only chat — every other capability has to be deliberately granted. Enforced server-side across roughly 25 endpoints; no credential, admin sessions, and pre-existing installs are unaffected.
  • Memory is the one permission that also gates reads — denying it means the account can't see shared memory content at all, not merely can't add to it.
  • The client hides too: restricted accounts no longer even see the Providers, Memory, Dream, WhatsApp, or Telegram tabs in Settings.
  • Known limitation: top-level Model Store/Calendar/Routines screens aren't hidden yet (the backend still blocks their mutating actions), and agent mode is still one flag shared process-wide — real per-account isolation is future work.

The CLI Closes Its Last Self-Hosting Gap

  • memo remote list-accounts/add-account/delete-account — account management over SSH, same pattern as device management, defaulting --role user and taking a --perm models,memory,... list where an unrecognized name is a hard error.
  • Two new inspection flags, deliberately scoped to pair with -chat <id>:
    • -chat <id> -list prints the chat's message history
    • -chat <id> -memory usage sums how many memories were injected per message — real persisted data, not a new measurement
    • -memory saved deliberately does not work: memory entries carry no record of which chat produced them, so an honest answer is impossible — the flag exists and refuses outright with an explanation rather than returning something plausible-looking but wrong.

Big Feature: Kilo Code Provider, With a Real Free/Paid Model Browser

Kilo Code's AI Gateway joins the provider list — models fetched live from the real API, never hand-typed, with free models sorted to the top under a green checkmark like OpenRouter. 368 real models, 18 correctly flagged free.

A real trap found by pulling live data before writing the parser: several of Kilo's "auto-routing" models report pricing "-1" — meaning not a fixed price, not free. Kilo carries a direct isFree boolean per model, used as-is.

OpenCode Zen got the same treatment (free marked by a -free id suffix — 64 models, 8 free). And once actually clicked through by a live user, the Provider screen itself turned out broken in three ways nobody noticed from reading code: Unicode glyphs instead of real logos, a dropdown that spilled outside the Settings dialog, and nine useless pre-seeded "Disabled" placeholder cards. All fixed — fresh installs start genuinely empty.

Security Fixes

  • Cloudflare Tunnel auth bypass, found live on a real Raspberry Pi deployment. The loopback exemption didn't account for a local reverse proxy forwarding external traffic to loopback — cloudflared's host-network container made every public request appear local. Anyone on the internet could reach every /api/ route with zero credential. Fixed by treating proxy-forwarding headers on an otherwise-loopback request as a don't-trust signal; the real desktop client never sets them.
  • Setup wizard reappearing per-origin — completion state lived only in browser localStorage, so multi-address installs re-showed the wizard. Completion is now a durable server-side fact.
  • H04 — ngrok binary download had no integrity check: a CDN error page served with 200 would have been accepted. Now verifies the response's leading bytes match the archive format promised.
  • H05 — _ acted as a SQL wildcard in WhatsApp message search, silently returning wrong results.
  • H10 — the agent audit log wasn't durable: entries lived only in a memory slice capped at 1000. Now appended to a persistent file on disk.

Small Fixes

  • Execute-bit regression found across multiple install paths: fresh server installs could land a non-executable llama-server. All paths fixed.
  • providers.json (with encrypted API keys) was silently excluded from uninstall backups — now included.
  • A SQL NULL crashed Stats on a completely fresh install before the first memory existed.
  • Switches in light mode rendered nearly invisible when off (Material 3 default styling) — explicit inactive-state colors from Memo's own theme tokens, verified in both themes.
  • Incognito mode could silently eat replies after a page reload: the UI never synced the backend's real incognito flag, showing a normal-looking chat while replies went into the incognito-only buffer. Fixed, plus a visible red tint and nav-rail indicator while incognito is active.
  • Mobile navigation redesigned below 600px: the desktop NavRail hides behind a floating hamburger drawer, the chat header collapses into a single overflow sheet, and EngineStrip gets out of the way. Verified live at 375px in both themes.
  • System tray icon with "Minimize to System Tray" (off by default), Dream got its own independently-configurable schedule plus a manual run trigger, Stats gained a "What It's Being Used For" breakdown ranked by actual token spend, and replies show a brain-icon badge counting exactly how many memories were retrieved.

Still Open

  • Orchestra + web search + agent off does no web search (no tool-calling to plug into).
  • Per-capability permissions hide Settings tabs only; top-level screens aren't gated yet, and agent mode remains one process-wide flag (real isolation is planned future work).
  • No telegram_send agent tool exists yet; the Telegram bot token isn't in export/Drive backup yet.
  • Built-in TLS, and starting/stopping tunnels from the CLI, remain not done (carried over).
  • ngrok's binary download still has no true checksum pin (see H04).

Thank you for using Memo. Feedback and bug reports welcome at github.com/BugraAkdemir/memo