Repository navigation
v4.100.66
The change protocol has always held what every file said when the open transaction started — that copy is the undo the rollback is built on. Until now only the rollback could read it, so a model that damaged a file had to rebuild the original from memory. This release hands both halves to the model.
read_files with revision: "base" — the file as this transaction found it. Ranges, line numbers and truncation go through the same windowing as a working read, so the two can be laid side by side. No read receipt is recorded for it: those line numbers are the pre-edit ones, and crediting the read would let the editor accept an edit to lines of the working file the model has never seen.
restore_file — one file back to that state, discarding your changes to that file and nothing else. A file the transaction created is deleted, because that is what putting it back means. A path that never existed is refused without spending budget. A file that already matches the base is refused and told so — that is a model that has lost track of what it changed, and the answer it wants is that the fault predates it.
Both appear only while the change protocol is armed. read_files is decorated inside the protocol session rather than given a permanent parameter, so a host running without the protocol keeps byte-for-byte the schema it had: there is no base revision without an open transaction, and advertising one anyway teaches a call that can only be refused.
Bounded, on purpose. Three restores per transaction, reset on a new one, with the remaining budget stated on every call. A cheap undo lowers the price of a reckless edit, and this model class has been measured issuing twelve rm of the file under test in one transaction and losing the file when the twelfth outlived it. A restore loop that announces itself is the point — the failure it replaces was silent and cost two hours.
Why now. Measured on the run that prompted it: 7328s and three discarded transactions, with 22 of 43 edits going to a line the same transaction had already edited, and 91% of the wall clock falling after the model first wrote "I accidentally deleted the entire dDec method. Let me restore it correctly" — restoring, from memory, a 400-character minified line. The snapshot next door had it exactly.
Known limit. forgetReads, which retires a restored file's read receipts, is wired in the VS Code host. The CLI's receipts are created inside createDefaultExecutors and are not reachable from the runtime host, so there it is unset and the warning in the tool's own result is all there is.
Also in this release: a tool call nobody could read is not a turn that called nothing
Ollama's qwen parser hands a block it cannot parse back as content rather than failing the request — which is right, since a turn may already have run commands and there is no safe automatic retry. What it leaves behind is a turn that looks, to everything downstream, like a model that called nothing. It is not: the model tried to act, and Your last message contained no tool calls is false in the one way most likely to make it do the same thing again.
Measured on the run that prompted this: eight such turns, 526s and 16,552 output tokens, one discarded transaction, and the raw <tool_call> left standing as the run's Completed message.
The defect is diagnosable rather than guessed at. In all eight the model opened a call, got part-way through a parameter, abandoned it without a closing tag, and started the whole call again — and the count of unterminated parameters matched the count of extra <tool_call> openings exactly, every time. The parser cannot tell an abandoned attempt from the value of the parameter it was abandoned inside. Each of the eight ended in a complete, well-balanced call: the model got it right on its final attempt and was told it had emitted nothing.
So the nudge now names the function the model was reaching for, counts the restarts back to it, and says plainly that nothing ran. And a garbled call no longer ends the run once the silence budget is spent — once per run, gated like every other extension of the nudge policy, so a host that set the budget to zero still ends on a silent turn.
Detection is narrow on purpose: an unterminated parameter is the evidence, and a nested <tool_call> alone is not. A model editing a file about tool calls writes that tag inside a parameter it then closes properly, and prose that merely mentions the tag names no function at all. Both have tests.
The Modelfile was never the problem — RENDERER qwen3.5 and PARSER qwen3.5 are both set, and ollama's qwen3.5 parser delegates tool calls to the qwen3-coder one, so the dialect was the expected one. The remaining fix belongs in that parser: treat a <tool_call> opening inside an unterminated parameter as a restart and parse the tail. All eight were recoverable that way.