Skip to content

Releases: Fangyuan025/Chaty

v2.1.7

Choose a tag to compare

@github-actions github-actions released this 06 Sep 23:50

A long run that stops starting over

A prompt is resumed from the engine's cache up to the first token
that differs from last time. On the architectures the Qwen3.5
family uses that is all-or-nothing: three quarters of their layers
keep a recurrent state that cannot be rewound to a midpoint, so
anything the cache cannot account for throws all of it away and the
conversation is read from the beginning. Six things in code mode
were doing exactly that.

The clock sat in the system prompt, to the minute, so a turn that
began in a new minute matched about twenty tokens and re-read the
rest. The answer that ended a turn was never written down, so every
continuation started cold — and the model could not see what it had
last told you. Tool results arrived as if you had typed them, which
a Qwen3.5 template counts as a new request: the model would
announce that you had sent one halfway through the work it was
already doing, and repeat your request back at you. The closing tag
of a tool call went missing, and one round threw away 5006 cached
tokens over the four it could not account for. Two paths recorded a
turn their own way. And a repeated call — usually progress,
re-running the tests after an edit — turned reasoning off for one
step, which re-renders the whole history, and then back again.

After: a median of 99% of each prompt resumed from cache on both
engines, across turn boundaries as well as within a turn. A first
turn still costs a full read, so does a conversation crossing the
point where its history must be compacted, and so does the first
turn after the app restarts — the cache lives in memory and does
not outlive the process. The turn after that resumes 99% again.

Speculative decoding, on models that bring their own head

Some checkpoints carry a small block trained to guess the tokens
the model is about to produce. The model then checks a whole run of
guesses in one pass instead of one pass per token, and every token
that survives is one its own sampler drew — the reply is the reply
it would have given either way. Both engines can drive it now.

It ships off, and marked experimental. Measured, it is a large win
on text whose continuation is obvious and roughly a wash on
ordinary prose, and nothing readable before a reply starts says
which one it will be; a default that is sometimes slower is not a
default. The switch is in Settings → Model, offered only on models
that actually carry a head — read from the file, not assumed.

Three things that reached v2.1.6 as a rebuild are in here for
anyone who installed that version on release day: a chat reply now
survives the interface reloading under it, the composer stops
losing a line to a scrollbar at 110% and 120% UI scale, and the
error log can be emptied from Settings.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.1.6...v2.1.7

v2.1.6

Choose a tag to compare

@github-actions github-actions released this 03 Sep 18:52

A reply that outlives its window

The interface can be replaced under a running turn — the webview
reloads and takes the JS context with it — and until now the reply
went with it. Three things made that certain. The llama.cpp sink
returned the failure from sending to a listener that had gone away,
so the first token after the page disappeared ended the turn. The
reply existed nowhere else, because tokens accumulated in that
page's memory and reached the conversation only at the end. And
there was no way back into a turn already running.

The app holds the turn now. Nobody listening is not a reason to
stop generating. The text is written to the conversation when the
turn ends, however it ends. And a page that comes up asks whether
one is in flight: if it is, that conversation opens on its own,
everything generated before this page existed is already in it, and
the rest arrives as it is produced. A reload also used to replay the
request that was in flight, starting a SECOND generation for the
same reply — two at once on one model, the newcomer empty and
holding the slot, so the page came back to a reply starting over
from the beginning while the real one carried on unseen.

A click that lands

Driving a real lesson on a real site, question after question,
turned up a failure an agent cannot escape on its own: a click that
reports success without having reached anything. A control still
settling — a popover animating in, a scroll container snapping
back, a banner sitting on top — is somewhere else by the time the
event is sent, and the event lands on whatever now occupies that
spot. Told it worked, the model has no reason to try anything else,
and it clicks the same button until the turn dies. The target is
now armed before the click and asked afterwards whether it fired. A
miss names what is in the way, and a coordinate the hit test
rejects is never clicked at all.

A choice can also be clicked by the words that are actually on
screen. An answer in a list wears a badge the page adds — 1,
2), 3. — so the element carries "2 jolie" while the page reads
"jolie". Asking for what you can read fell through to a substring
match, where the panel wrapping every choice matched too, and won:
the click landed on the panel, well away from any option, and
nothing was selected. Among elements that match, the one carrying
the least text beyond what was asked for now wins — anything
wrapping a control always carries more — and a leading list badge
is ignored on both sides.

The element list says what state a control is in, too. A greyed-out
submit read exactly like a live one, and an answer already chosen
like an untouched one, so a model clicked a dead button over and
over and lost track of what it had picked.

A session that stops dying quietly

Several long-lived things in Chaty could die in a way nothing
noticed and nothing recovered from, leaving a restart as the only
way out. One browser_close used to end browsing for the rest of
the run: the closed browser's actor cleared the cached handle as it
exited, by which time the next browser had already been cached, so
it wiped its successor's. Navigation then reported success while
every read came back blank, because the read happened in a window
that had never been navigated anywhere.

A browser left open by a previous run — after a force quit, or a
crash — held the profile, and Chrome will not start a second
browser on one profile, so the launcher waited out its deadline for
a debugging port that was never coming. That window is now adopted
when it can be reached and asked to quit when it cannot.

Speech that stopped stayed stopped: the output context was replaced
only when it reported closed, and WebKit has another state that a
route change or the machine sleeping reaches. One panic inside a
query poisoned the conversation database's lock, and the app
quietly stopped reading and writing conversations. One panic inside
a synthesis did the same to voice.

Faster, and smoother to listen to

Every drain of the browser's event queue blocked on a read timeout
that was empty nearly every time, and the settle loop paid it on
every poll. Draining what has arrived instead, leaving as soon as
the page goes quiet, and gathering the page digest in one round
trip instead of three: on a real site a click went from 4.1s to
0.47s, a page read from 130ms to 8ms.

Spoken replies are stitched on the audio clock. Each clip used to
be built only after the previous one's end had made its way back to
the main thread — which, during a spoken reply, is streaming
tokens, rendering and driving IPC — so a whole event loop landed at
every sentence boundary, exactly where it is most audible.

Under it

The thinking menu ticks the rung the model will actually use: the
chosen rung is remembered per app while ladders differ in length,
so one picked on a four-rung model could be absent from the
three-rung one you switched to, and the engine was handed nothing
at all. Six engine load failures — the out-of-memory refusal among
them — now speak the reader's language instead of English. Bilibili
links work again: a browser user agent without the cookies a
browser carries is the fingerprint that endpoint screens for.
Finished background jobs stop accumulating for as long as the app
is open. And a model that addresses each span to a recipient no
longer flashes that recipient as answer text at the head of every
reply.

Two fixes that reached v2.1.5 as a rebuild are in here for anyone
who installed that version on release day: code mode lights the
thinking depth you chose again, and a multimodal MLX model arrives
with the processor configuration its vision needs.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.1.5...v2.1.6

v2.1.5

Choose a tag to compare

@github-actions github-actions released this 31 Aug 00:49

Muse-Glimmer, on both engines

Neither engine knew this architecture. llama.cpp had no
muse-glimmer at all and refused every GGUF outright; MLX answered
unsupportedModelType. Both now run it — chat, code, vision, and
its four native reasoning rungs — and they agree on what they
produce.

Two of the departures are invisible in the checkpoint, which is
what made an implementation built from shapes alone produce noise.
The attention scale is qk_scale_factor / head_dim, the folded form
of scaling the QK-normalised queries and then letting attention
scale again; the same key names a different quantity in the two
configurations this model ships under, and reading one convention's
constant through the other's formula leaves the attention 11 times
too sharp. The token embeddings also pass through a
parameter-free RMS norm — parameter-free, so it leaves no weight
behind to notice is missing. Both weight layouts load: a packaged
MLX artifact fuses the attention gate into the queries and has its
norms folded, while a multimodal export keeps them apart and names
the four norms positionally, so the renames must not cascade.

It reads images. A 50-layer vision tower attends inside
32×32-patch windows except on every fourth layer, and a learned
position table is resampled to whatever grid the image actually
has. Each image expands to <|image_start|>, one token per merged
patch, then <|image_end|> — the markers are not decoration:
without them two images share one unbroken run of placeholders and
the cache cannot tell where the first one ends. With them, a
follow-up question after two pictures reuses 68% of the window,
the same as the other engine.

Its reasoning arrives as reasoning. ATEM addresses each span to a
recipient rather than tagging it, so a whole turn's deliberation
used to be shown as the answer. It now travels in a field of its
own, which is where the model's own template looks for it.

A model's own thinking ladder is drawn as it is

Chat listed 高 twice on a four-rung model, because anything that was
not low or medium took the same label — high and xhigh now have
names of their own. Code had the ladder written into the component:
four fixed tabs mapped to three rungs, so a four-rung model lost one
outright. The tabs now come from the model, however many rungs it
has. Off stays Chaty's, because a ladder has no rung for not
thinking at all.

Under it

llama.cpp moved forward to a build that carries the architecture,
and every local GGUF was re-checked on it: ten models, unchanged
answers, prefix reuse from 75% to 93%. A model it refuses now says
why — whether the file is not a GGUF at all, or the build does not
know its architecture, or the converter wrote the model's name where
the architecture belongs. It also says why an embedded chat template
was rejected instead of silently falling back to ChatML, which is
what let a model speak the wrong protocol while looking like it
worked.

A multimodal MLX model now arrives with its processor
configuration. The downloader named the files it would take one by
one and that list never had processor_config.json in it; the
sidecar can synthesize a replacement for the few families it has a
recipe for, which is what made the loss look intermittent rather
than constant — vision worked on Qwen and Gemma and went quietly
missing on everything else.

The renderer no longer reports a restart that did not happen: a page
replaced within the first seconds of a window's life is the window
opening, not a crash. And the interface is now available in
Brazilian Portuguese, placeholders included — thanks to the
community contributors on those PRs.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


What's Changed

  • i18n(pt-BR): complete UI localization (100% coverage) by @magisph in #10
  • i18n(pt-br): translate remaining placeholder strings by @magisph in #11

Full Changelog: v2.1.4...v2.1.5

v2.1.4

Choose a tag to compare

@github-actions github-actions released this 28 Aug 20:23

Chinese speech, and a picture that stops costing the conversation

Chaty listens and answers in Chinese. Speech recognition was
English-only and text-to-speech had no Chinese voice at all, so the
whole voice section was hidden outside the English UI. Recognition
now runs multilingual Whisper when Chinese is in play, and a Chinese
reply is spoken by a local VITS voice while English keeps Kokoro,
decided per utterance. English speech did not get worse to make room
for it: multilingual Whisper is a real accuracy drop on English, so
English stays on base.en — the model already on disk, no
re-download and nothing orphaned — and the multilingual model is
fetched only when the UI is Chinese or the Chinese-voice switch is
on. Its files are pinned to a revision rather than to a moving
branch, so a size check can no longer pass on bytes that changed
underneath it. (#8, by @wisetwo)

Around that: a voice model left half-downloaded now repairs itself
one file at a time instead of being fetched again from the start,
files land in a staging directory and are moved into place
atomically, the preview plays — WKWebView applies Safari's
user-activation rules, so the audio context is unlocked on the first
gesture rather than after the synthesis round-trip — and a preview
or transcription that fails says why. Live Mode stops racing the
microphone: the capture thread signals when it has actually released
the device and the next start waits for that, instead of guessing at
a delay.

A picture stopped throwing the conversation away. Through MLX, any
prompt carrying an image was evaluated from the very beginning — the
whole transcript, every turn, on the screenshot round and on every
round after it. Measured across seven local models in both thinking
modes: 0% of the window reused from the moment a screenshot entered
a conversation until the end of it. Two causes, both fixed. The chat
template rewrites a stored turn's reasoning when pixels force it to
render the history, and re-encoding a turn's text is not the inverse
of generating it — on Qwen3.5 2B the word "Chaty" comes back as a
different pair of tokens than the model emitted, and one pair is
enough to end the match. Chaty now replays the exact tokens each
turn occupies in the cache. Same sweep afterwards: 99-100% on every
round after a picture, all fourteen combinations.

A second screenshot resumes the conversation instead of re-reading
it. That round used to start over, because a call carrying pixels
was assumed to be positionable only from the beginning. It is not —
the model takes its positions from the cache — so only the new
picture is read now and the conversation beneath it is kept. The
older screenshot also stays visible instead of being dropped to save
an encode that no longer happens.

And a screenshot in a long conversation stopped taking minutes. The
span a vision model reads in one pass ran from the start of the
prompt to the last picture in it, so a screenshot's cost was set by
the transcript underneath: 12.3k tokens took 187 seconds on a 35B,
and a real 50k round had produced nothing after ninety minutes with
20 GB of swap churning. The text below a picture is ordinary text
and now goes in chunks like any other; only the picture's own span
is one pass. A 48k-token conversation with a four-tile screenshot
reads in 13 seconds against a warm cache and 131 cold.

A turn that stops saying anything is now cut. Not a runaway thought
— the think budget is the only ceiling on how much a model may
reason, and that stays. This is the other failure: output carrying
no information at all. A 35B step ran to 31,416 tokens of "!" at 1.1
tokens a second, twenty minutes of it, because the only ceiling was
the token cap. Four hundred characters of one repeated character now
ends the step, the wreckage is kept out of the transcript rather
than handed back as a pattern to continue, and twice in a row pauses
instead of grinding. Separately, when the window tightens, the file
bodies inside earlier write_file calls are replaced by a line
naming the file — in one session four such turns were 82% of the
transcript, and the file is on disk if the model wants it back.

A PDF the text extractor cannot read no longer kills the read. The
extractor asserts, rather than errors, on font encodings it has not
implemented — the encoding most Chinese-authored PDFs use is one of
them. The whole document is still tried first, and where it gives
out the pages are read one at a time, keeping every page that parses
and saying how many it could not: one textbook went from nothing at
all to 38,295 characters. The same guard covers attachments, browser
downloads, and knowledge-base indexing, where one bad file used to
be able to end a whole run. A PDF with no text in it — a scan — now
says that, instead of coming back as an empty document.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


What's Changed

  • voice: add reliable bilingual speech support by @wisetwo in #8

New Contributors

Full Changelog: v2.1.3...v2.1.4

v2.1.3

Choose a tag to compare

@github-actions github-actions released this 27 Aug 11:08

A long conversation stops starting over

Chat mode re-wrote its own compaction summary on every single
message. A fresh wording each time, sitting in the system message —
the opening of the prompt — so everything behind it moved and the
engine could match nothing it had already computed. Measured on
Gemma-4 26B through MLX: 99% of the window reused on the turns
before compaction began, and 0% on every turn after it, plus a whole
extra generation per turn to write the summary again. The summary is
kept now until the conversation outgrows the room it left, and it is
written from the previous summary rather than from a transcript that
gets more elided each time. Same measurement afterwards: 99-100% on
the turns in between.

Reasoning kept in its own field counts against the window again.
Some templates read a turn's thinking from reasoning_content
rather than from the message body, so Chaty splits it out for them —
and the budget counted only the body, reading a whole reasoning
history as very nearly free. Qwen3.8 27B is the one local model that
splits it, and in a fourteen-model sweep it was the one model that
walked off the end of its own window: six turns in a row answered
with "context" and generated nothing, the conversation simply dead,
while the budget still reported room to spare. Same shape as
pictures counting for nothing before 2.1.2. Afterwards the same ten
turns never overflow, and each turn recovers on the next one.

Today's date and the web-search results moved out of the system
message and onto the turn that produced them. A regex on the current
question decided whether the date line was there, and the results
were new every turn — so one question containing the word "recent"
cost two full re-prefills, the turn that added the line and the turn
that dropped it again: 2% and 1% reused, against 81% and 94% around
them. An answer that cited a source now keeps the source, too.

Code mode's cross-turn trim leaves room to grow. It freed exactly
enough to slip back under its ceiling, so the next turn was over
again — the same ceiling-hugging that mid-turn compaction was fixed
for in 2.1.2, on the other side of the turn boundary.

Thinking and web search can no longer both claim to be on. They are
mutually exclusive — a searching turn is sent with reasoning
suppressed — and turning search on from the command palette left a
tick beside Thinking in the Tools menu while every later turn
quietly stopped reasoning. If some of your turns seemed not to
think, this was why, and it was never the model.

A turn with no answer in it now says so. The prompt outgrew the
window and nothing was generated, or the model reasoned to the end
of its budget and stopped before writing anything: all of it used to
land as an empty bubble, or as a turn that vanished on reload.

A GPU load that was quietly cut back says so. After a driver crash
the next load takes fewer layers, and only the bottom of that ladder
was ever reported — the rungs in between just ran slower for no
stated reason.

The knowledge base answered out of six chunks, whatever size your
library was — the count was written into the search with a ceiling
of twelve above it and nothing in the app could raise it. It is a
setting now. Indexing a large file could also take the app down: a
file with no blank line in it is one paragraph, and the chunker
pulled the whole thing into memory four bytes a character first.

Code mode's deep / standard switch does something now. On any model
without a native effort ladder the rung was one bullet in a
six-thousand-character system prompt, while the off switch rode on
the last user message — which is where the model decides whether to
think, and how much. Measured across five paired tasks: deep used to
produce 0.95x the reasoning of standard on Qwen3.6 35B and was the
longer of the pair once in five; it now gives 1.52x there (5 of 5),
1.81x on Qwen3.5 9B and 3.20x on Gemma-4 E4B through the other
engine.

"Continue" after a paused turn no longer restarts the loop it was
breaking. A turn pauses when the model keeps repeating a call or
keeps leaving out a required argument, and everything working
against that — the heat, the rung the missing-argument ladder had
climbed, the repeat count — lived in the turn and died with it. So
Continue began the next one at base temperature, on the gentlest
rung, facing a transcript in which the model had just made the same
empty call five times. The rung carries over now, the first step
samples fresh rather than replaying, and the resumed turn says which
loop it came from and what to do instead.

Code mode's step and command-timeout ceilings can be switched off —
they stopped at 96 steps and 300 seconds, with a second 600-second
ceiling behind the slider that it could not even express.

The console tool could not see what Chrome was showing. Everything a
page spawns — a cross-origin iframe, a window it opens, a worker —
reports on its own debugger session, and only the first was attached,
so those errors existed in the browser and nowhere else. Reading the
console also emptied it, so a model that looked twice was told it was
empty while the error was still on screen.

A tall page's screenshot no longer fails the round on Gemma-4, which
through MLX cannot take more than one picture in a prompt: a
full-page capture arrives as several tiles, so every one of them
failed, and the retry behind each failure is what a browsing session
looked like from the outside.

A long unattended run stops eating the renderer. Every step card held
the full tool result — up to 384 KB for one file read, for every
step, written to disk on each save — which is how a run grows until
the web content process is killed and comes back empty: no error, no
notice, the turn gone and the mode reset to chat. Reasoning in code
mode is also shown the way chat shows it, a short window pinned to
the newest line instead of a trace that pushes the steps off screen.

On Windows, a GPU crash lowers the offer instead of ending it
(issue #9). A 26B model on a 12 GB card took the Vulkan driver down
on its first load and the reporter spent every session afterwards on
the CPU with no way back — the guard was a tombstone that no code
path anywhere removed, and the offload had been sized from the card's
total memory rather than what was free. A crash now halves what the
load was attempting, and the first load that survives clears it.

Settings hover explanations stay inside the window. They were drawn
on the label they belonged to and clipped by the panel's own edge,
which no amount of clamping could fix; they are real elements now,
outside the panel, measured rather than estimated.

v2.1.2

Choose a tag to compare

@github-actions github-actions released this 23 Aug 02:18

The model stops re-reading what it just did

Between turns, code mode threw away everything the agent had
actually done. The follow-up arrived with the tool results gone and
"(tools run: read_file, bash)" in their place, so the model no
longer knew what any of those calls had returned and re-read files
it had just read. The exchange now carries over intact — and that is
cheaper than discarding it was: 2035 of 2058 prompt tokens reused,
75ms, against a cold 208ms for the summary it replaces.

A follow-up question no longer re-reads the conversation on MLX
either. Chaty lays a conversation out one message at a time now,
from a layout learned off the model's own template and used only
when it reproduces that template's token ids exactly. Qwen3.5 2B and
Gemma-4 26B both go from 0% to 99% reuse on the second turn's first
step — 1587ms to 95ms, 4292ms to 216ms.

Thinking off no longer costs a full prefill every turn. Chaty
prefills an empty reasoning block after the assistant header, but a
stored turn was rendered without it, so round two diverged right
there — 0% reuse on Qwen3.5 and LFM2, and thinking off is code
mode's default. Now 100%, on both engines.

LFM2's tool calls run. The engine rendered control tokens as
nothing, so <|tool_call_start|>[read_file(path='x')]<|tool_call_end|>
reached Chaty as ordinary prose and the call never fired.

A screenshot the model has already seen is no longer re-encoded.
Three consecutive tool-result rounds on Qwen3.5 4B cost 2826ms,
5596ms and 5741ms before; they now cost about 150ms each. Dropping
stale screenshots turned out to make llama.cpp slower, not faster,
so it keeps them.

Context compaction condenses the work with a model-written summary
instead of a 60-character index per turn, and leaves room to breathe
rather than skimming the limit: the same task on an 8k window went
from 63 rounds and 633 seconds to 18 rounds and 132 seconds. Chat
and code share one token budget now, calibrated against what the
engine actually charges, and attached pictures count toward it.

A follow-up question no longer re-reads the conversation on MLX
either: Qwen3.5 2B and Gemma-4 26B both go from 0% to 99% cache
reuse on the second turn's first step (1587ms to 95ms, 4284ms to
217ms).

A screenshot the model has already seen is no longer re-encoded.
Three consecutive tool-result rounds on Qwen3.5 4B cost 2826ms,
5596ms and 5741ms before; they now cost about 150ms each, and a
fresh screenshot encodes only itself. Dropping stale screenshots
turned out to make llama.cpp slower, not faster, so it keeps them.

A chat reply now travels the way the model wrote it — stripping the
reasoning out of stored turns meant every reply re-read the whole
conversation. Attached pictures count toward the context budget.

Context compaction now condenses the work with a model-written
summary instead of a 60-character index per turn, and leaves room to
breathe rather than skimming the limit: the same task on an 8k
window went from 63 rounds and 633 seconds to 18 rounds and 132
seconds. Chat and code also share one token budget now, calibrated
against what the engine actually charges.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.1.1...v2.1.2

v2.1.1

Choose a tag to compare

@github-actions github-actions released this 22 Aug 06:16

An agent turn stops re-reading the conversation

In code mode, prefill used to crawl as the rounds piled up: the
prompt could no longer reproduce what the model had just generated,
so the cache died at the first assistant turn every step — and a
model whose memory cannot rewind answered that by re-reading the
whole conversation. A turn is now recorded so the next prompt is a
true append, and how to record it is probed per model on each engine
rather than assumed.

Second agent step, reused prompt tokens: Qwen3.5 0.8B 0 → 233,
Qwen3.8 27B 0 → 202, Qwen3.6 35B-A3B 0 → 233, Qwen3.5 4B (GGUF)
0 → 62. Only the genuinely new tail is computed, and the reused span
grows with the transcript instead of the cost doing so. Gemma 4, QwQ
and the Qwen3 line report identical counts before and after.

Thinking off no longer costs a full prefill every turn: the empty
reasoning block Chaty prefills after the assistant header was
missing from stored turns, so round two diverged right there. 0% KV
reuse on Qwen3.5 and LFM2 before, 100% after — and thinking off is
code mode's default.

LFM2's tool calls run. The engine rendered control tokens as
nothing, so <|tool_call_start|>[read_file(path='x')]<|tool_call_end|>
reached Chaty as ordinary prose and the call never fired.

A screenshot the model has already seen is no longer re-encoded.
Three consecutive tool-result rounds on Qwen3.5 4B cost 2826ms,
5596ms and 5741ms before; they now cost about 150ms each, and a
fresh screenshot encodes only itself. Dropping stale screenshots
turned out to make llama.cpp slower, not faster, so it keeps them.

A chat reply now travels the way the model wrote it — stripping the
reasoning out of stored turns meant every reply re-read the whole
conversation. Attached pictures count toward the context budget.

Context compaction now condenses the work with a model-written
summary instead of a 60-character index per turn, and leaves room to
breathe rather than skimming the limit: the same task on an 8k
window went from 63 rounds and 633 seconds to 18 rounds and 132
seconds. Chat and code also share one token budget now, calibrated
against what the engine actually charges.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.1.0...v2.1.1

v2.1.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 01:16

What the model actually wrote

The MLX engine stops altering the model's own output. Every CSS
class selector was losing its leading dot — .card { reached the
screen as card {, so a page the model had written correctly
opened with no styling — on every MLX model, not one family. The
rendered prompt no longer gains a newline the reference never had,
so the same weights answer the same prompt they would anywhere
else. Nine local models across four families now render prompts
identical to the reference implementation.

Numbers and files describe what you actually have: opening a canvas
in the browser reuses the file already there instead of leaving a
copy behind, and Settings → Data stops counting messages nobody can
open (84 of 94 on one machine) while model and database sizes are
no longer understated.

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.0.9...v2.1.0

v2.0.9

Choose a tag to compare

@github-actions github-actions released this 16 Aug 19:00

Highlights — the model's own thinking dial

Qwen3.8 runs on both engines, with its native
reasoning-effort ladder (low · medium · xhigh) wired into the UI
— a submenu on Chat's
thinking item, and Code mode's switch turned into the native rungs. On MLX the rung travels as
the chat-template kwarg; llama.cpp accepts no custom kwargs, so Chaty rewrites the rendered
prompt into the requested rung, byte-identical to the official render. The ladder is detected
from the template rather than the model name, so renamed finetunes keep it — and every model
without one keeps exactly the toggle it had.

Measured on Qwen3.8-27B (8-bit MLX), same question and seed: low 24 s · medium 31 s ·
xhigh 125 s. Five times the wait, or five times the deliberation — your call, per message.

Also in this release: the tiktok-video skill picks up upstream's Ken Burns judder fix, and the
settings sliders stop guessing what your model can do — the think budget, per-step output limit
and GPU-offload ceilings now come from the loaded model's context window and layer count.


Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.0.8...v2.0.9

v2.0.8

Choose a tag to compare

@github-actions github-actions released this 12 Aug 03:05

Install

Platform File
Windows x64 Chaty_*_x64-setup.exe — per-user installer, no admin
macOS (Apple Silicon) Chaty_*_aarch64.dmg
Linux x64 (beta) Chaty_*_amd64.AppImagechmod +x and run. First Linux release — please report anything odd

⚠️ macOS first launch — "Apple could not verify…" / "damaged"

Chaty is ad-hoc signed but not notarized (no paid Apple Developer
account), so Gatekeeper flags it on first launch. The app is safe —
everything runs locally. Clear the download quarantine once, in Terminal:

xattr -dr com.apple.quarantine /Applications/Chaty.app

Then open Chaty normally. (Alternatively: try to open it once, then go to
System Settings → Privacy & Security → Open Anyway.)


Full Changelog: v2.0.7...v2.0.8