mindfork 0.9.9 — files in, charts out, work in the background
mindfork 0.9.9 is out. It is a release about keeping the conversation moving: the assistant's code gets your files and hands its own back with no copy-paste in between, a delegation no longer holds the chat while it runs, and the engine is one command away instead of a build away.
Your files reach the code, and its files reach you
Until now the sandboxed Python could compute, but only on what the assistant
typed into the script. Now a call names the chat's files it needs — by the
number /file list shows or by name — and each is copied into the sandbox
before the code runs: an attachment as its text, a stored file or a picture as
itself. The copies are the sandbox's own, so nothing here changes under the
code's hands, and only what it saves into /w/out comes back. That folder is
the other half: a matplotlib chart, a CSV or a workbook saved there is kept
with the chat, listed by /file list, packed by backups — up to ten files and
50 MB a call — and a PNG or JPEG is shown to the model, so it can check the
chart it drew. Measured before shipping on Qwen 3.6 27B and Gemma 4 31B: a
plot colour only the image carries was named nearly every time with the
picture and never without — and blind, both models described a chart they had
not seen, which is why every image the assistant is not shown is now said in
the result, in words a small model does not talk past.
/file attach stops refusing a binary: a workbook or a zip is kept with the
chat, readable by code alone, and a PDF, DOCX or web page keeps its original
beside the text the assistant reads — one entry in the list, removed together,
never your own file. /file open <name|#N> hands a file to whatever your
system opens it with, and /file folder opens the chat's folder. Only
document types open directly; a script, a shortcut or an HTML page the
assistant wrote opens the folder it sits in instead, so nothing the
assistant produced can run by being opened — and on Windows a workbook or
document the code wrote carries the same mark as a download, so Office opens
it in Protected View.
The same contract holds when Python runs on your own machine instead of in the
sandbox: the files named in the call are copied in, what the code saves comes
back, and a new per-process memory limit (Windows, off by default) turns a
runaway allocation into a MemoryError instead of taking the machine's
memory. The sandbox itself gained sympy, networkx, lxml, pyyaml, regex,
feedparser, openpyxl, pypdf, tabulate, pillow and matplotlib —
mindfork sandbox setup fetches only what is missing — and its packages are
now packed into an image the code cannot alter, so one call can no longer
leave a file that runs inside every later one. An existing sandbox needs that
command run once more; the tool names it when it refuses.
Work that goes on without you
A delegation used to hold the conversation until it landed. Switch on
Background runs in Settings → Tools and the assistant gains
start_subagent: a delegation that returns at once — the assistant keeps
answering you, past the end of its own reply — and whose result arrives later
as a task notification in the chat. When the chat is open and idle the
assistant reads it and reports by itself; otherwise the note waits for your
next message. The run is a row of the chat list while it is out, its transcript
opens as it streams, and a result that lands while you are looking elsewhere
marks its chat unread. A staged dialogue (below) can run the same way. A
background run never asks you to confirm a tool call — switch off the tools you
would not let run unattended.
F7 (or /tasks) is the screen for all of it: every subagent and dialogue
run across all your chats — the ones running, with the round and tool they are
in; the ones that landed, with how they ended — and the app's own quiet work
(reflection, consolidation, history compaction) as running, waiting or idle.
Enter opens a run's transcript, F6 stops one — the app's own tasks
included — and /tasks stop <kind> does the same from a terminal where F6
never arrives. A reflection stopped before it wrote anything gives its window
back, so nothing is skipped and nothing is written twice.
Things can also happen at once. Subagent: parallel runs lets the
delegations of one reply run together; Parallel tool calls lets the reads
and page fetches of one reply run together — only tools that change nothing,
four at a time on a cloud engine by default, one after another on a local
engine until you raise it; and a new Parallel sessions setting on every
engine mode says how many request streams the app may keep open. A managed
llama-server is then launched with -np N --kv-unified, its slots sharing
the one context pool, no extra memory. Parallel runs and sessions both default
to 1, so nothing changes until you turn them up. Measured on one RTX 4090: two
sessions gain little on a 31B model, four gain 2.9× on Qwen 3.6 27B. Every
stream reserves what it will occupy in the pool and waits for room rather than
overfilling it, so two subagents no longer die together with "Context size has
been exceeded" after a minute of visible progress.
The app's own requests learnt to yield. A title, a reflection or a history compaction that is streaming when your message arrives is cancelled and made again after your reply, rather than holding your message until it ends — measured, a message that waited 46 s on a CPU-only server now waits about 24 s, one that waited 5.6 s on a GPU under two. A CPU-only host runs a smaller prompt batch for the same reason, and the app reads the server's own timing of every prompt: once per server session it says in the feed how fast prompts are processed and the one setting to change when that is slow.
A scene of two characters
run_dialogue stages a conversation between two personas the assistant
composes, and the assistant directs it as it unfolds — a private stage
direction to one character, a retake, a line rewritten outright — and ends it
when the scene reaches its ending. The script is a child chat under the
conversation, each side under its character's name, the director's
interventions visible as notes; open it while it runs and each line streams in
on its speaker's side. One engine session, one request at a time, so no extra
VRAM on a local model; a run-time limit (30 minutes by default) bounds a scene
that will not end. Gemma 3's chat template, which refuses two same-role turns
in a row, is handled — for the director's memory of its verdicts, and for
Ctrl+U impersonation, which used to answer with an engine error on every
chat you had opened yourself.
The engine, downloaded
mindfork llama backends lists the llama.cpp llama-server builds published
for your OS and architecture — cpu, vulkan, cuda-13.3, rocm-10.0, … —
with their download sizes, and mindfork llama setup --backend <id> fetches
one into data/llama/. Every file is checked against the sha256 the release
publishes, an interrupted download resumes instead of starting over, and a
CUDA build brings the CUDA runtime with it — without it the backend does not
load and the server quietly runs on the CPU. Once unpacked, the binary is run
and reports its build number and the compute devices it found, so a GPU
backend with a missing driver says so instead of pretending. --set-binary
writes the path into the settings; mindfork llama installed and
mindfork llama remove <id> manage what is on disk, and the remove refuses
while a setting still points at the build.
Better still, the llama-server binary field may now be left empty: the
app takes the build installed last, or a llama-server sitting next to the
application — so downloading one, or unpacking a llama.cpp archive beside
mindfork, is enough to run in managed mode with nothing typed.
Pick up where it stopped
/continue resumes an interrupted reply in place — one cut by Esc, by a
dropped connection or by the length limit: the model picks up exactly at the
cut, the text grows inside the same message, and a turn interrupted between
tool calls resumes its tool loop. It works on the local and external engines,
on Gemini, and on Claude models up to the 4.5 generation; the providers that
cannot resume a partial reply (OpenAI, Grok, current Claude models) are told
apart, and the command says so instead of guessing. A reply that hit the
length limit finally gets a note at all.
An external server's model is now named on screen even when you did not
type it — the app asks the server what it is running — and the "Model (opt.)"
field, which had never actually been sent, now reaches the server:
llama-server in router mode, LM Studio, LiteLLM and OpenRouter all pick the
model from it.
Off until you say so
- The web tools start off.
web_search,fetch_urlandyoutube_watchare disabled in a fresh installation: the search engines behind them are picked by the app and the query is built from your conversation, so nothing goes to them until you switch the tools on in Settings → Tools. An existing installation keeps whatever its settings already say. - A search key is only used where you put it. A
TAVILY_API_KEYsitting in your environment for some other program is no longer picked up on its own. Name the variable, or enter the key, in Settings → Tools. - The sandbox's largest download is verified too — the Python
distribution
wasmerfetches on the app's behalf, until now the one file not checked against a pinned checksum. - A privacy policy, readable in the app:
F1→ the tab that used to be "Disclaimer" is now Legal and carries both documents, in the interface language. The Windows installer shows it and every archive ships it. It says what stays on your machine, what leaves it and which setting has to be on first, and what reaches the author — nothing. - The spellcheck dictionaries say where they come from, with their
licences installed beside them;
en_GBmoves to its author's current word list, about 14 000 more stems; and an installed build gets adictionaries/folder of your own in the data directory, with a note on how to add one.
Also in this release
- Files in an older encoding are read — and edited — as they are. A windows-1251, KOI8-R or cp866 text, or Notepad's "Unicode", used to reach the assistant with every letter replaced by the replacement character, and an edit in an attached project rewrote the whole file that way. Files are read in their own encoding now, an edit is written back in it — refused with nothing written when the encoding cannot hold the new text — and a web page in an older encoding is read as it declares itself, or recognised from its text.
- A fetched page is named after the page, not its site — every chapter of the Rust Book used to be attached as "The Rust Programming Language".
- Spellcheck no longer stumbles over a stress mark: a word carrying one is judged whole, stressed text is left alone, and a real typo under the mark is still underlined.
- The assistant can name its language model.
get_llm_nameanswers "which model are you?" with the model actually generating the reply, andget_llm_historylists the profile's dated history of model changes — filled from your existing chats on the first launch, as far back as they go. - The self-model screen (
F3) reads as two named halves — Assistant and User, or the names from the profile — with the newest observation first; a setting flips the order. - Every key hint names only keys that work, sits in the same place on
every screen and follows what you have selected, and
F1is among the hints everywhere. The status bar no longer stacks its hints into a column while a turn runs, and says cancel forEscwhile a reply is generating. - An API-key field says whose key it is — OpenAI API key, Tavily API key (env, opt.) — everywhere one is entered.
- The chat list counts messages the way the conversation reads: your questions and the assistant's replies, not every tool call's result.
- A long title no longer ends mid-word: it is cut where it is drawn, to the columns that screen has, always with "…" — and titles an older limit had cut on disk are repaired.
- A tool round on OpenAI no longer fails when the model reasons in stages, and a server error inside an open stream ends the reply as an error — with the retry that was always meant to run — instead of passing for a finished reply cut mid-word.
- In KDE Konsole the footer names the line-break chord the terminal can
actually deliver (
Alt+Enter), instead of one it silently swallows. mindfork.execalls itselfmindforkin Task Manager and Explorer, with the author and copyright it was missing; the installer carries the real version instead of0.0.0.0.- The Python sandbox installs and runs again. The Python package is pinned
to an exact version, after a build published in the Wasmer registry in late
August could not be compiled by the runtime the app pins. A sandbox
installed in that window needs
mindfork sandbox setup --force.
One note on data: chats gain a list of stored files, and the data folder a
files/ directory beside chats/; chat files move to schema 4 — a version
stamp for dialogue transcripts, then the repair of titles an older limit had
cut. Both migrations happen automatically on the first start, after a
pre-migration backup, and nothing is removed; an older build refuses to open
migrated data rather than misread it.
The full list is in the
changelog.
Downloads — Windows installer and archive, Linux archive, .deb, .rpm and
.pkg.tar.zst — are on the
releases page.