Thetis is an agent whose loop, tools and chat interface are WebAssembly components it can rewrite while it is running. Every change is compiled, validated, versioned, and reversible.
Thetis was a shapeshifter, and Peleus won her by keeping his hold through fire, water, lion and serpent until she was herself again.
Every agent you can buy arrived finished. How it works and what it can reach were settled at build time, by strangers, for everyone at once. Thetis leaves those questions open and works them out in the conversation, with the person having it.
Say a price on some page matters to you, and it has no way to read that page:
it writes itself the tool mid-reply, compiles it, and calls it before it answers.
Say the timestamps under every message are clutter, and it edits the chat
interface it is serving you, and the clutter is gone. Work that comes round
again can be written down as a skill, a markdown file in skills/
that it reads back into any conversation that needs it. None of this is a
feature request. It happens between one message and the next.
The same mechanism keeps it current with the rest of the world. A model released this week, or a service it has never heard of, is something it can integrate for itself, in place, without waiting on anybody's roadmap. The evidence is the shelf of tools it has already built: BigQuery, Notion, a browser it drives page by page, git plumbing, web search. None of it came in the box.
The gains hold. Every conversation runs on its own branch of the source, so an experiment stays where it was made; the changes that earn it get merged, and every conversation after that one starts from the merged version. A month of small requests leaves a month of small permanent improvements behind.
Six months in, the thing answering you is one that nobody shipped.
All of which stays survivable because a change has to compile, load, and pass a smoke test before it can go live, and trunk only advances when a person merges it.
The forms are the guests, and the contract that holds them while they change
is the grip, written once in wit/thetis.wit.
One native binary, the orchestrator, is the trusted kernel. It owns the network, the filesystem, the database and the build toolchain, and hands guests narrow, mediated slices of them through that contract. Guests hold no ambient authority: everything they can observe or affect is an import declared in the file. Changing the file rebuilds every guest, so it stays orchestrator-owned and the dev kit refuses agent writes that touch it.
Guests are instantiated per call, in a fresh store. That is what makes hot swapping safe. A turn already in flight finishes on the component it started with, and the next call picks up the new one, so no turn ever straddles two revisions and a swap needs no quiet moment to land in.
Every aspect is a component built from source in the repository, and every one of them can be replaced while the process keeps running.
agents/agent-core runs the agentic
loop: rehydrate, prompt, stream, dispatch tools, check for nudges. It is also
the source the agent edits when it changes itself.gateways/gateway-web serves the
chat UI and owns the wire protocol.tools/<name>, scaffolded from
templates/tool-template and edited by the agent.These tools appear in the model's tool list whenever the dev kit is available. Every mutating one rebuilds the target immediately and returns the compiler's verdict in the tool result, so the model reads its own build errors and fixes them inside a single turn instead of waiting for a human to relay them.
| Tool | Effect |
|---|---|
new_tool(name, description) | Scaffolds a tool crate, builds it, loads it |
write_code(target, path, contents) | Replaces a file, rebuilds, hot-swaps |
patch_code(target, path, old_text, new_text) | Exact-match patch, rebuilds, hot-swaps |
read_code / list_code | Inspection |
add_dependency / remove_dependency | Edits a component's dependencies, rebuilds under a timeout |
branch_status / branch_log | Where this conversation's branch stands, and its history |
reset_branch(rev) | Restores the branch to rev as a new commit — nothing rewritten |
update_from_trunk | Brings the latest trunk into the branch; conflicts come back marked |
target is self, tool:<name>, or
gateway:<name>. A tool created this way is callable on the very
next loop iteration. A change the agent makes to itself takes effect when the
current turn ends, so the turn that writes a new loop finishes on the old one.
Every conversation runs on its own git branch of the source tree, and every green
build lands there as a commit. One verb is deliberately missing: merging to
trunk. Landing a conversation's changes is a human decision, made in the UI.

live · the agent patching its own gateway on branch conv/e8f75dfb, 41 commits ahead — every prompt 97–100% cached
The system is built so that no self-modification can make it unreachable.
Writes are constrained too: paths cannot escape an aspect's source tree, and
devkit.protected_files can put any file off limits. The grip
itself, wit/thetis.wit, is always refused — the agent can rewrite
what it does, never what it is allowed to reach.

live · /admin on a running instance — the trunk log, every commit authored by thetis, merged by a person
The web UI is an ES-module app with no build step, embedded in the gateway
component. Adding a file means one line in assets.rs; adding a
client action means one function in handlers.rs and one entry in its
dispatch table.
Each conversation carries a mode, Agent by default.
Plan withholds every tool that would change something and refuses
those tools at dispatch too, so a model that remembers a tool from earlier in the
conversation still cannot call it. Models are a per-conversation override chosen
from THETIS_MODELS. Skills are markdown files in
skills/, attached per conversation and appended to the system
prompt; editing one takes effect on the next turn, with nothing to restart and
nothing to register. The tools panel shows what the model is offered here, asked
of the agent itself rather than reconstructed, so it cannot drift from reality.
Images can be pasted, dropped or picked from the file browser, and travel
base64-encoded end to end.
Cache hits show in the transcript under each reply, because a saving you cannot see is one you cannot trust. On a three-turn conversation with the full tool surface, the opening turn cost $0.0106 and every turn after it reported 99% of its prompt served from cache, at $0.0010.

live · the surface mid-rewrite: branch panel 41 ahead, merge → trunk waiting on a human, terminal on the worktree (conversation list blurred)
A conversation can fan out. spawn_agent starts a child session with its own event log, context window and turn loop, working in the same checkout, so the parent pays for the conclusion instead of every step that produced it. Children cannot spawn children: unbounded fan-out is one of the recurring ways multi-agent systems collapse, and one level keeps the parent identifiable as the thing responsible for the work.
Notes saved with remember survive restarts and self-modification. Compaction sheds the middle of a long conversation, keeping head, tail and every user message, and never edits the event log. The log is append-only; a compaction records which spans it stands for, and rehydration projects the log through those records.
The tool surface is scoped to what a conversation looks like it needs, because large flat catalogs cost accuracy as well as tokens. tool_search is always offered, even in read-only modes, so the escape hatch cannot itself be scoped away.
Terminals open on registered ssh hosts, from a registry the publish filter never sees. The same agent answers in Discord, where every session is stamped read-only and there is no command to change that.

live · the conversation where delegation itself was built — conv/b9311d3c, claude opus 5, $52.81 of work
the shelf so far — tools/ in the repo, every one scaffolded in a conversation
Be clear-eyed about what enabling terminal access means: the agent can run
anything you can. Sessions keep their working directory and state between
commands, and filesystem.enabled,
filesystem.allow_delete, terminal.enabled and
control.allow_restart each turn a slice of that off.
The roots are the boundary. Every path is resolved, symlinks followed, and
must land inside filesystem.roots, which defaults to the project
root alone.
Two things in the README are listed under Not yet built. The Docker
exec sandbox is defined and wired but stubbed, so with
THETIS_SANDBOX=false the agent is not offered code-execution tools
rather than handed tools that fail. MCP imports exist and return empty; no client
is connected yet.
Requires the Rust toolchain with the wasm32-wasip2 target.
rustup target add wasm32-wasip2
git clone https://github.com/biscuitWizard/thetis.ai.git thetis
cd thetis
Set an OpenRouter key and start it. Guests are built automatically on boot.
OPENROUTER_API_KEY=sk-... cargo run --release -p thetis
Then open http://127.0.0.1:7777.
A scriptable stand-in speaks enough of the streaming protocol to exercise the whole system at no cost: token deltas, tool calls, usage accounting.
cargo run --release --bin mock-llm
OPENROUTER_API_KEY=test OPENROUTER_BASE_URL=http://127.0.0.1:7788 \
THETIS_MODEL=mock/echo cargo run --release -p thetis
From there, the docs cover the rest: getting started, architecture, self-modification, recoverability, what reaches beyond one conversation and configuration.
Peleus did not stop the changing. He kept his grip while it happened, and the shape that came out the other side was the one he wanted. Thetis changes all day, under a contract that never lets go.