Anvil v2.2.21 — Durable Daemon Sessions, the Relay Bridge, and the Big Modularization

v2.2.21 is the largest single-release reshape Anvil has had since v2.0. Session execution moves out of the TUI and into the anvild daemon — close your terminal and the session keeps running; reopen anywhere and reattach. A browser can now pair to a live session over an encrypted relay and drive it. Underneath, the biggest modularization pass in project history split the codebase into 31 focused crates, three of the planned performance wins landed, and a cluster of P0 daemon bugs were found and fixed under live verification. FreeBSD and NetBSD x86_64 now ship as prebuilt binaries.

The product story in one line: Anvil stops being a TUI that writes some background files and becomes a daemon that hosts sessions, which a viewer attaches to.

Durable Daemon Sessions (D.1–D.4)

The headline architectural shift: session execution now lives in anvild, not the TUI process. The session protocol contract (SessionRequest / SessionEvent) defines the on-wire types; a SessionActor hosts the conversation inside the daemon; the TUI is repointed as a thin viewer over a Unix socket. Close the TUI — the session keeps running. Reopen it — or attach from elsewhere — and you reattach to the same live session. Crash the daemon, restart it, and the session replays from the journal.

Multi-client attach means more than one viewer can watch the same session, with peer-attach / peer-detach events so each viewer knows who else is present.

Named, Multi-Tab Sessions (#911)

Daemon sessions became first-class and human-addressable. The session manifest gained a name and a last_active_at_ns; a Rename IPC verb carries it on the wire. The TUI drives the full lifecycle — /session list|rename|kill over one-shot IPC, then /session new|open|load|close mapped onto TUI tabs with per-tab daemon routing. The capstone is same-cwd auto-resume: reopen Anvil in a directory that already has a live daemon session and it reattaches instead of starting cold.

Relay Bridge — Attach From the Browser (#914, #917, #919–#921)

A remote viewer (the AnvilHub web app) can now pair to a live daemon session over a passage WebSocket and drive it. anvil routined relay start spins up the bridge and auto-opens the viewer in your browser; pairing returns a full PIN.

  • #917 (P0) — fixed a daemon-side tool-dispatch name-drift bug where every tool fell through to a stub. Daemon-hosted sessions now execute real tools.
  • #919 — viewer UX: input echo, a thinking spinner, and live context; viewer input to an unmapped tab starts a session on demand.
  • #920/permissions works end-to-end over the relay.
  • #921 — a ring recorder makes resume/replay carry the actual conversation, so a viewer pairing (or re-pairing) mid-session sees the history, not a blank pane.

Performance — Pooling, Parallel Tools, Faster Search (#898, #899, #900)

Three of the five optimizations scoped for this release landed:

  • #898 — shared reqwest::Client across the workspace. No more fresh TLS handshake + DNS lookup on every API call; the client is reused, so HTTP/2 connections stay warm.
  • #899 — parallel read-only tools. When the model requests two or more read-only tools in one batch (read_file, grep_search, glob_search, web fetches, and friends), they fan out across worker threads and merge back in original order so the transcript stays valid. Mutating tools still run sequentially through the permission gate. A regression test pins three 100 ms tools finishing in under 200 ms.
  • #900 — gitignore-aware streaming search. grep_search now walks and searches simultaneously, respects .gitignore, and skips binary files — so it no longer drowns in node_modules/ or target/.

The Big Modularization — 31 Crates

The largest reshape in project history. anvil-cli was split into anvil-ollama, anvil-mcp-builder, anvil-tui, and anvil-wizard. Thirteen crates were extracted from runtime across two rounds — anvil-vault, anvil-journal, anvil-permissions, anvil-oauth, anvil-hooks, anvil-mcp, anvil-memory, anvil-curator, anvil-search, anvil-relay, anvil-reflection, anvil-skill-chain-exec, and anvil-routines — with intermediate crates breaking the dependency cycles. runtime shrank from roughly 70K to about 57.7K lines; the workspace now has 31 crates. Supporting work: an anvil-e2e integration-test crate and compile-time locale-key drift detection.

Daemon Hardening — P0 Fixes (#902–#917)

The daemon execution path was load-bearing for the first time, and live verification surfaced a cluster of P0s — every one fixed: wiring the real provider runtime into the daemon (#905), connecting the attach forwarder task (#906, with an e2e gate that would have caught it in #910), binding REPL session I/O to the long-lived runtime (#903), reading the correct PID file (#904), and a /heal probe that checks actual process liveness (#902). Auto-compaction now actually compacts (#913). And the security P0 from the arc: anvild binary downgrade-prevention (#827).

Seven Platforms — FreeBSD and NetBSD Join

v2.2.21 ships prebuilt binaries for macOS (ARM + Intel), Linux (x86_64 + ARM64), Windows x86_64, and now FreeBSD x86_64 and NetBSD x86_64. IPC gained a dual-transport abstraction (Unix sockets and Windows named pipes) so the daemon runs natively across all of them. FreeBSD ARM64 and OpenBSD remain pending — the Rust toolchain doesn’t yet publish a precompiled standard library for those targets.

Quality

3,926 tests passing, zero failures. Several latent shared-state test races were tracked down and serialized along the way — process-global locale, env-var, and atomic flags that only flaked under specific parallel scheduling. The build profile was tuned (LTO was trialed, then disabled after it tripped a toolchain codegen bug on the x86_64 cross-target; strip stays for the size win). The result is a clean, reproducible build on every one of the seven platforms.

Compatibility

v2.2.21 is a drop-in upgrade from v2.2.20. Config, vault, and session formats are forward-compatible — no migration steps required. The daemon-default execution path is opt-in for now: sessions still run in-process unless you pass anvil --daemon. If you have stale anvild processes from binaries you’ve moved or trashed, kill them before upgrading — anvil --update will refuse to run while stale daemons are alive and tells you the exact PIDs.

Install

brew upgrade anvil

# or, fresh install:
curl -L https://anvilhub.culpur.net/install.sh | sh

Seven platforms, SHA256-verified, single binary, no runtime required. The full release lives at github.com/culpur/anvil/releases/tag/v2.2.21.

Close the terminal. The session keeps running. Reopen anywhere and reattach. That’s v2.2.21.

Scroll to Top