Repository navigation
v0.3.0
Added
- Hosted apis are first-class. Anthropic gets a native transport
(/v1/messages) with prompt caching on the system prompt, the tool
schemas and the end of the history. OpenAI, Google's OpenAI-compatible
endpoint, OpenRouter and the rest work through the existing OpenAI path.
A profile picks the protocol withapi = "auto" | "openai" | "ollama" | "anthropic". - Per-profile
headers, for endpoints that do not take a bearer token
(Azure'sapi-key, gateways with their own). - Running cost. Set
price_in/price_out/price_cachedon a profile
and the status bar,/statusand-pshow what the session has spent. /modelsnumbers the list, and/model 3picks from it.multi_edit: several replacements in one file in a single call, applied in
order, all of them or none. One round trip instead of one per change, one
diff to approve, and no way to leave a file half edited./undo. Every file a request changes is snapshotted before the change,
and one request is one checkpoint, so/undoputs all of it back at once
(/undo listshows what is there). A file someone edited after thoth
wrote it is reported and left alone rather than overwritten, and the last
20 checkpoints live under~/.thoth/projects/<key>/undo/so a crash does
not take them with it.move_fileanddelete_file. Renaming and deleting used to mean reaching
for the shell, which skips the read registry, shows no diff, and turns one
"always allow" into a standing permission to runrm. Deleting needs the
whole file read first (the same condition as overwriting it), only works
inside the working directory, and never touches a directory.todo: the plan for the task, written before starting and rewritten as it
goes. It keeps a model from losing track of step three of five, and lets
you see the plan is wrong before the work is done. Only the latest version
of the list stays in the context.- The config screen asks which endpoint a new profile talks to (anthropic,
openai, gemini, openrouter, ollama) and fills in the url, so only the
model and the key are left to type. - Named config profiles.
thoth config(orthoth cfg) opens a screen to
edit them,/configdoes the same inside a session and applies the saved
profile to the running conversation.thoth -P NAMEruns one profile
once,thoth config use NAMEmakes it the default,thoth config list
shows what exists. Config files from 0.2 and earlier keep working; they
are read as a profile nameddefault.
Changed
- Tool calls the model writes as text (
<tool_call>{...}</tool_call>, or a
bare json object) are read and run instead of printed. A json object only
counts when it names a tool that exists, and anything that does not parse
comes back to the transcript untouched. - What a request costs is now budgeted against the context window instead of
fixed at numbers tuned for 16k: how much of a tool result is kept, how much
of a fileread_filereturns, and how much of the instruction file goes
into the prompt. A small window keeps its room, a large one gets to use it. grep,web_fetch,list_dirandglobcut their own output to the
budget too, the same wayread_filedoes, so the line that says the
result was incomplete is not itself the thing that gets cut off. They had
fixed limits of their own (11k characters, 15k, 500 entries) that ignored
how much room there actually was.read_filedoes its own cutting, so its "showing lines 1-240 of 900, read
on with offset=241" survives. It used to be replaced by a blind truncation
that left the model with no idea there was more, or how to ask for it.- Repeating a read-only call with the exact same arguments replaces the
older result in the context with a one-line note. Two copies of the same
file are one copy of dead weight; a read of a different range, or a search
with a different pattern, is left alone because it says something else. - The prompt ends with one worked example of a small task from grep to final
answer, and says how many tool calls are left for the request. Small models
follow an example better than another rule. - The style rules now say what not to write: no listing the edits again, no
repeating the plan just carried out, no explaining code that was not asked
about, and an answer as long as the question deserves. - Rules about git and about the editor's
problemstool are only sent when
there is a repo and an editor to use them on, and the tool itself is only
offered then: 800 characters of every request that was buying nothing. - Auto-compact works on every api, not just Ollama: it measures against the
profile'scontext_window. The field used to be callednum_ctx, which
is still read. - thoth no longer picks a model on its own when the endpoint is hosted. A
local server usually has one or two and guessing is a kindness; a hosted
one has hundreds and guessing spends the user's money, so it lists some
and asks. - The Ollama probe only fires at a local address. A hosted endpoint never
sees a request to a path thoth guessed at. stream_optionsis an OpenAI extension that not every compatible server
takes. When one rejects it, thoth drops the field and retries instead of
failing the turn: losing the token counts beats losing the answer.- The config file moved to
~/.thoth/config.tomlon every OS, next to the
state thoth already kept there, instead of the platform config directory
(%APPDATA%\thothon Windows,~/.config/thothelsewhere). The old path
is still read when the new one is missing, so upgrading changes nothing
until the next save. @pathnow opens a picker under the input instead of completing on tab
only: up/down move, tab or enter takes the highlighted entry, esc closes
it, and picking a directory lists what is inside it.- Startup screen: logo, version, working directory and, on the Ollama
native api, the context window that used to be a transcript line.
/clearshows it again.
Fixed
- The directory an "always allow" covered for a file outside the working
directory was recorded as the model spelled it, so reading../notes.txt
saved a grant ending in/..: no later path matched it, and/allow
showed the user something they could not place. Found by running it. globstopped at 500 matches and said nothing about it, so a truncated
list read like the whole answer. It says what it stopped at now.- The editor context (active file, selected text, the Problems panel) went
into every request unbudgeted: a large selection or one page-long type
error could take a good part of a small window. Both are capped, and say
when they were cut. - The permission preview for overwriting a file with CRLF line endings
showed every line of it as changed. It now previews what will actually be
written. - Every multi-line
edit_filefailed on a file with CRLF line endings,
which is most files on a Windows checkout.read_fileshows lines with
the carriage return stripped, so the text the model copies back could
never match the file byte for byte. Edits are now matched in the file's
own line endings, and a full overwrite keeps them instead of turning
every line into a change. --continueon a session that was killed between a tool call and its
result sent a transcript every api rejects, and nothing but/cleargot
past it. Half a turn is dropped when the transcript is loaded.- The
todotool refused a status it understood perfectly well: a model
writing "in_progress" or "Completed" instead of "doing" and "done" lost a
turn to a validation error. Those spellings are read as what they mean; a
word that means nothing here is still refused. - Security: one "always allow" for a file outside the working directory
covered every file everywhere for the life of the project. Reads and
writes outside it are now scoped to the directory the file is in; inside
the working directory one answer still covers the project. multi_edit,move_fileanddelete_fileshowed nothing once they were
always-allowed, unlikewrite_file,edit_fileandshell. Every one of
them now prints what it does whether or not it had to ask./undosaid nothing about a file it had not snapshotted because the
request was too large, so a request could come back looking complete while
one file was still changed. That file is now named and reported.- An undone checkpoint is kept, marked, instead of being deleted. For a file
that had been edited since and was therefore left alone, the copy in that
checkpoint was the only one left of what was in it before. - Security:
move_filecould carry a file in from anywhere or out to
anywhere, and needed no read first, which made renaming a way around
having to read a file before overwriting it. Both endpoints must now be
inside the working directory and the file must have been read, the same
bar as deleting it. Found by review of the commit that added it. - Security: the
delete_filepermission preview read the file before the
user had approved anything, including one outside the working directory
and of any size. It now refuses outside paths and reads only the first
few lines. inside_projectsaid "outside" for any path that did not exist yet on
Windows, where the canonical form of the working directory carries a
\\?\prefix that a plain joined path does not.
Internal
client.rswas 1600 lines holding three wire protocols; it is a module
directory now, one file per protocol (openai,ollama,anthropic)
with the shared message shape and transport choice inmod.rs. The two
text protocols shared 50 lines of copied stream handling, which is now one
TextStreaminclient/stream.rs.- The drawing half of the interface moved out of
ui/mod.rsinto
ui/screen.rs. A child module sees its parent's private fields, so
nothing had to be made public to split it.
Removed
- Google Programmable Search.
web_searchgoes through DuckDuckGo, which
needs no key and no account. Thegoogle_api_keyandgoogle_cx
settings are gone; search backends are worth revisiting as a whole later.
Full Changelog: v0.2.0...v0.3.0