We recently chose to take advantage of Bun's infamous "Rust Rewrite" and migrate the Traces CLI from Bun v1.3.13 to v1.4.2. traces now opens 45% faster and uses 23% less memory.
The startup gain comes from the Rust runtime itself, which initializes JavaScriptCore and loads our code faster on every platform. The memory gain comes from two changes in Bun 1.4. First, Bun moves JavaScriptCore allocation onto mimalloc. Second, Bun added a scavenger thread that returns memory to the operating system while JavaScript is idle.
Opening the Traces TUI is now 130 ms faster, which is a difference you'll notice. We definitely did.
There were also three significant behaviours that changed between Bun 1.3.13 and Bun 1.4.2
Every Traces adapter uses fs.watch to follow an agent's transcript file, and our live view depends on those events arriving.
Previous Bun implementations ran two watchers per subscription on macOS, a kqueue watcher and FSEvents thread. Bun 1.3.14+ rewrote fs.watch on POSIX systems using FSEvents only, but FSEvents have known-silent failure modes.
In this setup, a watcher that silently stops delivering events looks exactly like an agent that stopped writing, and there is no way to ask whether it is still working. If we'd shipped the upgrade without checking, a fraction of our Mac users would have opened Traces during a live session and seen nothing, and had no error to explain why.
We ruled out trusting the watcher, because it can fail without telling us why. We ruled out polling everywhere, because that punishes Linux users for a macOS problem. We ruled out adding chokidar because we didn't want the native dependency.
Instead, we paired the FSEvents watcher with a poll. Four of our adapters already did this. The others now share one module, which stats the file every 500 ms and fires the same callback the watcher would fire if the modification time or size changed. We tested it by stubbing fs.watch to install cleanly and never fire a single event, and all adapter and core tests passed.
Grok's tracks trusted folders in trusted_folders.toml. Each entry has a decided_at timestamp, which is a signed 64-bit number. JavaScript can't represent a 64-bit integer exactly, and Bun 1.4 stopped rounding those values. Intead, Bun.TOML.parse now rejects the whole file.
Traces never reads decided_at, so we don't parse it. Instead, we can it as text and strip it out first. We added a stripvalidatedDecidedAt step that checks each value is pa planin TOML integeger within the signed 64-but range and removes that line before handing the file to Bun.TOML.parse. Anything we can't safely verify this way is treated as an error rather than passed through.
traces setup wrote color codes into piped output. Bun 1.3.13's node:util.styleText ignored NO_COLOR and the TTY check. We'd written a terminal-style module that asks Bun.enableANSIColors instead, and every plain command was supposed to use it.
Bun 1.4 fixed styleText, and the fix exposed the gap. setup had been calling styleText directly for its intro, outro, and prompt help text, so it followed different colors rules than the rest of the CLI. We removed that import and routed setup through the shared bold and dim helpers and gave its prompt formatter an explicit color flag so tests cover both styled and plain output.
A runtime rewrite gives you performance without code changes, but the compatibility work isn't free. We'll take 45% faster any day. But this upgrade also sharpened our awareness of what can go unseen in a runtime change. When you upgrade foundational tools, the gains can be real, but keep your eyes on the details along the way.