v1.13.0
Release Notes - Mint Agent v1.13.0
ποΈ Plan Mode Now Saves Plans (CLI)
Plan mode previously showed an approved plan once in the terminal's approval
card and then discarded it β no way to reopen what was actually agreed on.
It now persists.
- Approved plans are saved to
<workspace>/.agents/plans/as a
timestamped Markdown file the moment you approve them, printing a
Plan saved to: ...line right after β mirrors the existing
.agents/skills/<slug>/SKILL.mdconvention auto-learned skills already
use, scoped per-workspace rather than global. /plan listshows saved plans (newest first) with a one-line preview
of each;/plan show <number|name>prints one in full./plan [on|off]
still toggles plan mode itself, unchanged.
β User-Message Dividers in Interactive Chat
Echoed user input in the terminal chat loop is now visually separated from
agent replies with a dim β divider across the full terminal width, drawn
above and below the You βΊ line. Applies both to messages typed in the
input box and to queued inputs (/send, piped stdin) β so long logs stay
easier to read at a glance.
π§ n8n Docs: Inline Auth Header + Non-Default Hosts
docs/N8N_INTEGRATION.md now puts the Bearer <key> inline in the
Authorization header for the mint mcp add n8n step β the old
AUTH_HEADER env-var trick relied on shell expansion Mint's verbatim
argument passing never performs. Also documents that /n8n with no
arguments only auto-opens the browser for a hardcoded 127.0.0.1:5678;
pointing the MCP URL at another host/port keeps /n8n <task> working.
This release adds a real-time 3D avatar you can point at Mint β connected
via a new /avatar command, driven by both automatic tool-call reactions
and a model-callable avatar_signal tool β on top of the same
polish-and-reliability work already below: a proper day/night weather icon
set, offline-safe self-hosted fonts (plus a real font bug fixed along the
way), a blank-page bug fixed on the web dev server, the shared "cli"
conversation now live-syncing across the terminal/desktop/web instead of
staying silent until your next turn, a workspace-scoping attempt that
shipped, broke that same shared conversation, and got reverted the same
day once caught β and the web build's approval flow finally does something
real instead of silently auto-denying every tool call, with OS/browser
notifications for both finished replies and pending approvals.
πͺ Real-Time 3D Avatar via Project Avatar (/avatar)
Mint can now drive a live 3D VRM avatar β via
Project Avatar, a free
open-source companion project β that reacts to what the agent is actually
doing, viewable in a browser tab, OBS, or Project Avatar's own desktop
overlay.
/avatarin chat (ormint avatarfrom the CLI): generates and
persists a relay token the first time you run it, then lets you pick
Web (opens a hosted share link β nothing to install) or Desktop
(hands you the raw token to paste into Project Avatar's own desktop app,
built separately from their repo)./avatar statusshows connection
state, selected model, and viewer count;/avatar offdisables it.- Automatic reactions: the agent's tool calls drive emotion/pose/prop
changes on their own β typing while a shell command or browser action
runs, celebrating when an image/video finishes generating, going nervous
on an error β with per-category cooldowns so rapid tool calls don't make
the avatar flicker. avatar_signaltool: for anything a tool call can't express β a
greeting, a joke, an apology β the model can call this directly to set
emotion blend, pose, prop, color, and the mouth/talking layer explicitly.
Only offered once a token is configured; an invalid call gets a
corrective message back instead of failing outright.- Multi-session aware: a
dispatch_subagentrun gets its own lower-
priority session on the relay, so a subagent's own tool calls don't
visually fight the main loop's current avatar state while both are
active. - Found and fixed three real bugs along the way: the CLI's own
interactive/mint agentchat loop was never actually wired to push
avatar signals at all (only the desktop app's streaming chat command
was) β chatting in the terminal silently did nothing until this was
caught and fixed; the desktop app's streaming command was separately
missing the turn-end/idle-reset call entirely; and the bridge's cooldown
timers were seeded at construction time instead of left unset, so the
very first signal after Mint started up could get silently dropped if it
landed within the cooldown window. All three now have regression tests.
π§Ή One Source of Truth for Interactive Slash Commands
Adding /avatar surfaced a pre-existing structural bug: /help's command
table and the / autocomplete dropdown were two separate hand-maintained
lists with nothing tying them to the actual command dispatcher, and had
already drifted apart β /edit-image, /gen-image, /shells,
/subagent, and a legacy /searchProvider alias all worked but were
missing from /help before this release touched anything.
- Consolidated into one table (
interactive/commands.rs) that both
/helpand the autocomplete dropdown now render from, instead of each
keeping its own copy. - Added a regression test that parses the dispatcher's own source for
every"/xxx" =>match arm and asserts each one is documented in that
table β it already caught the/searchProvideralias gap during this
same change, so the same class of bug can't silently ship again.
π€οΈ Weather Icons β Day/Night SVG Set
WeatherCard used flat lucide icons that didn't distinguish day from
night. Replaced with a detailed day/night SVG set, mapped from WMO weather
codes, so the previously-unused isDay flag now actually changes what
renders after dark instead of being dead data.
π€ Self-Hosted Fonts (and a Real Chat Font Bug Fixed Along the Way)
The Settings font picker's font families now ship with the app via
@fontsource instead of being pulled from fonts.googleapis.com at
runtime β desktop and web both render correctly offline instead of
silently falling back to system fonts whenever the CDN is unreachable.
- Found and fixed a real bug while wiring this up: picking a Font
Family in Theme & UI wrote todocument.body's inline style, but
#roothad its own explicitfont-familyrule grouped alongside
body/htmlβ nothing inherited frombody's override, so every chat
message stayed on the hardcoded default no matter what was selected.
Fixed by dropping#rootfrom that rule and switching the chat body's
own hardcoded font stack to inherit instead.
π©Ή Fixed: Blank Page on Refresh at Deep Chat Routes (Web Dev Server)
Refreshing on a deep route like /chat/<id> under npm run dev:web left
a blank white page. index-web.html's entry script used a relative src
(./src-web/main.tsx); Vite's dev server serves that file verbatim
(unlike a production build, which rewrites it), so on a deep route the
browser resolved the relative path against the current URL instead of the
site root, 404ing the entry script. Every other asset reference in the
file already used a root-absolute path matching this build's base: '/'
β this one was missed. Fixed the same way.
π The "cli" Conversation Now Live-Syncs Across Surfaces
Web/desktop and the interactive mint REPL already shared one local
SQLite DB and the same chat_id="cli" rows, but nothing surfaced a
message sent from one surface on the others until your next turn there.
- New lightweight DB-polling background task (
live_sync.rs): queues
a[synced] ...notice into the terminal's existing idle-tick notice
display β the same path/bgjob notices and linked-folder save notices
already use β whenever a new row appears from another surface, without
requiring the local API server to be running. - Closed a pre-existing writer/writer gap by setting a
busy_timeout
on SQLite connections, so two surfaces writing at the same moment no
longer race.
β οΈ Found and Reverted: a Workspace-Scoping Attempt That Broke the Shared "cli" Room
A same-day incident worth being honest about rather than quietly dropping
from the changelog: this release also shipped, and then reverted, an
attempt to scope the "cli" conversation by workspace.
- The idea: hash the active workspace path into the
clichat_id
(cli::<hash>) so opening desktop/web against a different project
wouldn't mix an unrelated project's history into the one shared
terminal conversation. - What actually happened: "workspace" in this app is a UI selection
the user switches often (the sidebar's Workspace picker), not a stable
per-process value like a terminal'scwd. Every switch produced a
different hash, fragmenting the one conversation meant to stay shared
across the CLI, desktop, and web into five-plus disconnected buckets
within hours β desktop showed an empty "cli" room, web appeared to stop
responding, and real chat history sat orphaned under the old unscoped
cliid. Confirmed by direct inspection of thechat_sessions/
interaction_memoriestables in the local sqlite DB. - The fix:
scoped_chat_id()now always returns the chat_id
unchanged β scoping is disabled, not deleted, so it can be revisited
later with a genuinely stable identity source if it's ever worth doing
again. Verified viacargo check/cargo testacross all three crates,
npm run typecheck, a live API server confirming history no longer
depends on which workspace happens to be selected, and a real browser
session confirming the "cli" room loads its full history again.
ππ Notifications for AI Replies and Pending Approvals β and Web Approvals Made Real
Two related gaps closed together: a finished reply or a blocked approval
prompt gave zero signal when the app wasn't focused, and the web build had
no real approval flow at all β /api/chat-stream's agent-mode branch
hardcoded auto-deny, so every tool call the agent tried silently failed
for web users with no way to approve it.
- Web approvals are now interactive: a new
PENDING_APPROVALSmap
(token β one-shot channel, mirroring desktop's existing approval
mechanism) lets the chat-stream endpoint send anapproval-requested
event down the same ndjson stream the reply/progress events already
use, then block until a newPOST /api/submit-approvalresolves it β
the sameApprovalCardUI desktop already had now actually renders and
works on web. Tokens areuuidv4, not sequential, since this endpoint
is LAN-reachable by default. - OS/browser notifications:
window.api.notifyAiResponse(preview)
fires (viatauri-plugin-notificationon desktop, the native browser
NotificationAPI on web) whenever a reply finishes or an approval
becomes pending while the tab or window is hidden or unfocused, and
clears again on refocus. - Verified end-to-end in a real browser session, not mocked: a real
WriteFilerequest got denied by workspace policy, the agent fell back
torun_shell, a genuineRunShellapproval card rendered, approving
it completed the stream, and the notification fired with the correct
description while the tab was unfocused.