An agent does not wake up in a room. It wakes up inside its context: the prompt, the files it has read, every tool response it has been handed. That context is the entire universe it can see, and every tool call builds that universe one response at a time.
Which makes a 169 KB edit response a strange thing to build with. Ask an agent to replace a 2,000-line file and muonry used to hand back the file it just wrote, echoed twice. Once as content, once as diff. The agent wrote that file seconds ago. A copy of its own writing does not help it see. It just crowds out everything else.
v0.2.7 treats every response as terrain the agent has to live on. Edits return a synthesized diff and a capped echo instead of the whole file. Search output is capped and grouped per file, and every cap announces itself. Reads decode hidden Unicode into a warnings field, and an opt-in scan keeps secrets out of the universe entirely. Four changes, one job: build the agent's world well.
89×
smaller edit echo
169 KB → 1.9 KB, 2,000-line replace
11.8×
less search output
351 KB → 30 KB, broad pattern
41×
faster auto-diff
8.7 ms → 0.21 ms, no git fork
1.3×
faster edits
20.3 → 15.7 ms per op
TL;DR
A whole-file replace now returns 1.9 KB of metadata plus a capped diff instead of 169 KB of echo. A broad search returns 30 KB instead of 351 KB, grouped per file, with an explicit marker for everything it left out. Reads warn about Unicode tag blocks, confusable characters, and, with MUONRY_CRYPTO_SCAN=1, private keys and seed phrases, without ever echoing the secret itself.
Edits: synthesize, don't fork
A diff is how an agent sees its own hands. It cannot watch a file change on disk; it knows what an edit did only through what the response tells it. That is why, since v0.2.4, every edit response has carried a unified diff: verification without a follow-up read. The way we produced that diff was lazy, though. We forked git diff against the working tree after every write. That cost a process fork per edit. It showed the cumulativeworking-tree diff rather than this edit's delta, so any other uncommitted change leaked into the response as stale noise. And it produced nothing at all outside a git repo, or on a dry_run.
The fix was already inside the daemon. It holds everything a diff needs: the pre-edit snapshot of the file, the content the agent sent, and the resolved line range. v0.2.7 synthesizes the diff from those three things in-process. The diff step drops from 8.7 ms to 0.21 ms, edits as a whole get 1.3× faster (20.3 to 15.7 ms per op), the diff is always exactly this edit's delta, and plain directories and dry-run previews get one too.
The same release caps the echo: 40 lines of written content, 120 lines of diff, with explicit *_elided_lines fields whenever a cap fires. That is where the 89× comes from. A whole-file replace used to echo the entire file twice, once as content and once as diff. On a 2,000-line file that was 169 KB of response. It is now 1.9 KB.
Search: capped and grouped
Search is how the agent looks outward, and what search returns becomes the landscape it navigates. A low-selectivity pattern, say const in a Zig repo, used to answer with a flood: every match, each line prefixed with the file's absolute path. A flood is not a map. Two caps and one format change turn it into one:
limit(default 500 lines). The response carrieslimited:trueand a refine hint when it fires, and the engine stops walking once the cap is hit instead of formatting output it will throw away.max_per_file(default 25). A single hot file can't eat the whole budget; capped files end with a “+N more matches” marker so the agent knows what it didn't see.- Grouped output. Matches sit under one relative-path header per file with
line:contentrows below it, instead of repeating the absolute path on every line.
Together: 351 KB down to 30 KB on a broad pattern, 11.8× less output, and nothing silently dropped. A map that hides its edges is worse than no map, so every cap announces itself. One walk fix rode along. Vendored zig-pkg directories are now skipped like any other dependency dir, which took a broad regex sweep of this repo from 270 ms to 54 ms; most of the old time went to scanning a vendored sqlite3.c.
Secrets stay out of context
Some things should never enter the universe at all. Set MUONRY_CRYPTO_SCAN=1 and reads warn when a file contains them: PEM private keys, Bitcoin WIF keys, runs of 12 or more BIP-39 seed-phrase words, API secrets by prefix (Stripe, GitHub, Slack, AWS, Google, plus Infura and Alchemy RPC URLs), and wallet address-poisoning pairs. A poisoning pair is two different addresses that share their first and last four hex digits, the signature of a paste-the-wrong-address attack.
Two design rules held throughout. First, the warning never echoes the secret. Quoting a private key to flag it would place the key inside the very context we are guarding; only poisoning warnings show a masked 0xPPPP…SSSS form, since addresses are public anyway. Second, the scan is off by default. The seed-phrase pass is plain-ASCII work that would tax the clean-file fast path, so you pay for it only when you turn it on.
Scope, stated plainly: this is defense-in-depth on the read and search surface. It stops an agent from unknowingly echoing, logging, or committing a secret it read. It does not stop code that executes from reading keys directly. That is a job for a sandbox and a permissions layer, not a reader.
Measured against v0.2.6
Numbers in a release post should survive a rerun. After shipping, we rebuilt v0.2.6 from the commit just before the version bump and drove both daemons through pipe mode with byte-identical operations: a broad search, twenty single-line edits, and a whole-file replace of a git-tracked 2,000-line file.
v0.2.6 rebuilt from the commit before the version bump. Fixture: a git-tracked 2,000-line file for edits; pattern “const” over zigrep/ for search. Warm daemon, first iteration dropped as warmup.
Two notes on the numbers. The replace fixture lands at 20.5× rather than the headline 89× because its lines are long, so the capped 120-line diff stays fat relative to the old echo. The mechanism is the same either way: the v0.2.6 response carried the file roughly twice, a 97 KB content echo plus a 65 KB diff, while the v0.2.7 response carries no content echo at all and an explicitly elided diff. And the 30× on single-line edits isolates exactly what was removed, the 9 ms git diff fork that used to run after every write.
Update: v0.2.8, same day
v0.2.8 shipped later the same day with the CLI side of the toolchain: two features agents kept reaching for, and one bug that had been hiding in every tool since the day it was written.
- zigrep -g/--glob. Ripgrep-style file filters:
-g '*.ts' -g '!*.test.ts' -g '!thirdparty'. Bare!direxcludes prune whole subtrees during the walk, and globs containing/match the path relative to the search root. Works in content search,--files, find mode, and-Rreplace, where excluded files are guaranteed untouched. Runs inside the parallel engine: 5.4 ms where ripgrep takes 12.5 ms on the same query, 2.3× faster. - zigpatch --all. Replace or delete every occurrence of a pattern in one atomic write. Byte-level splice: a file without a trailing newline keeps that shape, a replacement containing the pattern cannot recurse, and
--dry-run,--backup, and--undocompose with it. 1,667 replacements across a 5,000-line file in 2.2 ms, ahead of sed at 2.8 ms, and roughly 1,900× ahead of the old one-match-per-invocation workflow. - zigpar scheduling. Batch barriers are gone: each op now waits only on the latest earlier op that touches the same file with a write involved. A manifest with one slow search and an 8-edit chain on another file drops from 36 ms to 21 ms, because the chain hides under the search. The new zigrep and zigpatch surfaces ride along in manifests:
globson search ops,pattern/allon edit ops.
The bug is the part worth remembering. The scheduler rewrite refused to get faster, and instrumentation showed an edit's elapsed time exactly equaling a concurrent search's, every run. Exact equality is synchronization, not contention: children spawned at the same moment were inheriting copies of each other's pipe file descriptors, so an edit's EOF waited for whichever sibling exited last. The toolchain already had a pipeSyscall() helper that set FD_CLOEXEC to prevent exactly this. It never worked on Apple Silicon: it called the genuinely-variadic libc fcntl through a fixed three-argument declaration, and on ARM64 macOS variadic arguments travel on the stack while fixed arguments ride in registers, so the callee read garbage where FD_CLOEXEC should have been. One line, declared variadic, and the edit that used to report 19 ms (the search's lifetime) reports 2 ms (its own), in all thirteen tools, including the diff drainer this very post describes. If you declare variadic libc functions with fixed-arg prototypes behind any C FFI, this bug is waiting for you on Apple Silicon.
Upgrade
Run muonry update to pick up the new binary, and restart your MCP client so it reconnects to the new daemon. muonry --changelog shows these notes inline.
Full release notes are on the changelog. Issues go to the GitHub repo.