AgentFileChange shape, the node chat stream renders each confirmed change as
an expandable diff, and the Diff tab works while the run is still executing
instead of only after it finishes.
The rest of the release is volume: 254 commits since 0.33.0, 97 of them fixes
for the places daily production use exposed cracks, notably resuming and
retrying runs, agent fallback chains, steer delivery, Gateway workflow-UI
discovery, and a run status that stops overstating how healthy a run is.
(0.33.1 was tagged but never published to npm; everything it contained ships
here.)
Disk use is now bounded and inspectable end to end. Active detached logs cap
themselves at 100 MiB with a preserved tail and truncation notice, while the
new bunx smthrs gc reports filesystem capacity and reclaims terminal-run
logs (including workflow-adjacent legacy logs), sandbox roots, and worktrees
behind age and ownership guards. Legacy
hidden campaign worktrees and known temp scratch shapes are surfaced but need
an explicit --include-unmanaged; live process directories and unpublished
worktree changes remain protected.
Upgrading
Live workflow supervision: steer, hijack, and a terminal cockpit
A long-running workflow used to be observable but not steerable: you could watch the logs, or kill the run. 0.34.0 adds a supervision layer with three levels of control. Steer.bunx smthrs steer RUN_ID "focus on the failing test first" queues
a durable instruction that lands as a user turn at the target node’s next
agent step; the run never stops. --node picks a specific node (the default
is the run’s current agent node), bare bunx smthrs steer auto-picks the
single active run, and delivery is tracked through SteerQueued and
SteerConsumed events with expiry at terminal states. Steers survive
restarts because they live in the store (_smithers_steers), not in process
memory.
Take over. bunx smthrs steer --takeover parks the run resumably and
hands you the agent’s live session to drive yourself. It is run-wide, so it
warns and asks before aborting in-flight sibling agents.
Cockpit. bunx smthrs supervisor (alias top) opens a live outline of
phases and agents: j/k to select, Enter to open a node detail tab (in herdr
when available), an approve/deny surface for a waiting gate, and [ ] to
switch runs. It sources through the workspace Gateway by default and silently
falls back to direct smithers.db reads when none is reachable.
With a herdr terminal server running, bunx smthrs herdr attach mirrors the run into a terminal workspace: a workspace per run, a
cockpit tab, and a tab or pane per agent node with attention promotion and
terminal outcome markers. bunx smthrs herdr status|attach|open|clean manage
the mirror. The client speaks herdr wire protocol 19 (herdr 0.8.0); a protocol
mismatch fails closed before any mutating call, and the optional mirror paths
degrade to silent no-ops, so a missing or older herdr never affects the run
itself.
Reasoning effort is now first class along the way: it is derived at spawn,
persisted per attempt (_smithers_attempts.effort), and rendered on both the
direct-db and gateway display paths.
Agent file edits are one contract, and the UI renders them as diffs
A CLI agent subclass may now implementparseFileChanges, which normalizes a
file_change action into AgentFileChange records. The same normalization
runs live inside createOutputInterpreter, which attaches the result to the
streamed action as detail.fileChanges.
AgentCapabilityRegistry.fileChanges (supportsFileChanges and
supportsUnifiedDiff); the per-engine table is in the
agents reference.
In the node chat stream that means:
- A change with a diff is an expandable row that opens the hunks inline.
- A paths-only change renders as a plain row labelled
diff unavailablerather than a focusable button that does nothing when you click it. - A started edit stays hidden until the tool result confirms it, so a denied or failed edit no longer looks applied.
- Claude’s
Writereconstructs a full-creation diff only once the result confirms the file was new. Over an existing file it stays paths-only instead of fabricating an empty old side, andNotebookEditreconstructs inserts only. - Kimi’s
WriteFilekeeps its file-change kind through tool-result completion, so the pending change finalizes instead of collapsing into a generic tool row.
Diffs while the run is still executing
getRunDiff and getNodeDiff refused until a run reached a terminal status,
so the Diff tab stayed empty for exactly as long as you cared about it. While
a run is live, both now diff the run base against the current working copy of
every checkout an attempt recorded, read-only, never cached, under the same
50MB oversized cap that produces DiffTooLarge. The bundle is flagged
live: true and surfaces as a (live) marker in the summary. The terminal
base-to-terminal diff stays the authoritative final snapshot.
The oneshot Diff tab and the TUI tree-mode diff tab refresh off run events
with a trailing debounce, because agent edits arrive in bursts, and take one
final refetch on the terminal transition. A refresh no longer flickers the
existing diff out for a spinner.
One account’s rate limit no longer stalls a run
The newfallbackAgents() turns the account registry into a ready-made
failover chain: one agent rung per registered Claude Code or Codex
subscription, shuffled per call so load spreads across seats, with your normal
agent appended as the last rung. On a machine with no registered seats it
returns just that agent, so single-account setups behave exactly as before.
ctx.runId and a run keeps the same chain across every
render and retry, which keeps the engine’s per-rung quota skipping precise,
while different runs still start from different seats. providers: "all"
extends the pool to Kimi, Antigravity, and raw API-key accounts, and
models/agentOptions apply per-provider overrides without ever letting a
rung be repointed at another subscription.
Oneshot builds the same shape from the same registry, in registration order
rather than shuffled: bunx smthrs agents add regenerates .smithers/agents.ts
so every usable registered account is its own chain rung inside its engine’s
pool, and the run walks those rungs before it moves on to the next engine.
Adding a second registration for a subscription you already registered is
called out at add time now, because a duplicate seat adds no capacity, only
confusion; the check compares within one provider, so a Claude seat and a
Codex seat that happen to share a sign-in email stay separate rungs with
separate rate limits.
Register every subscription you own, browser login included
bunx smthrs agents add --tmux handles the providers that only offer a
browser login. It launches the provider CLI in a detached tmux session with
the account’s config dir set (Claude Code via claude auth login --claudeai,
skipping REPL onboarding), prints the attach command so you can complete the
login in the browser, polls until the credentials land, and registers the
seat. Re-running after a timeout detects already-finished credentials and just
registers. The interactive agents add wizard offers the same flow whenever
tmux is available.
agents add also records who was signed in, and agents list shows that
account identity next to each label, so two labels backed by the same
subscription (and therefore the same rate limit) are visible at a glance
instead of looking like extra capacity. Registered agent ids are stable now:
registeredAgentId/registeredAgentLabel live in @smthrs/accounts as the
canonical home, and the CLI and runtime helpers stamp identical account-backed
ids.
Subscription headroom across every registered seat
The new privateapps/quota-dashboard workspace app is a compact Electrobun
desktop view over the real bunx smthrs usage --format json output. It charts the
weekly and session headroom for every registered Claude Code and Codex seat,
sorts the pool by available capacity, and puts expired or broken sessions first
so the account to re-authenticate is obvious.
agents list --format json now includes each seat’s non-secret signedInAs
identity, which lets the dashboard pair a quota window with the subscription
behind a local label. The compiled .app bundle stays ignored; only the source,
configuration, and both refreshed lockfiles are committed.
ForkFanOut: end-of-run chores on a fork of the finished session
<ForkFanOut> fans independent chores out over forks of one task’s final
agent session. Every entry waits for the source task, starts from a copy of
its conversation snapshot in a fresh session (the source is never mutated),
and runs its own prompt. It is built for the end of a run: linters, named
commits, memory writes, and logging that need full context of the work just
done without depending on each other.
agent/output/label overrides over the
component-level defaults, an allowlisted set of Task props passes through,
and duplicate ids or a missing agent/output/fork fail at render time
instead of mid-run. Details are in the
ForkFanOut reference.
Resume and retry work again
retry-task ran the engine in the foreground with no detach, so a closed
pipe (SIGPIPE) killed the run and orphaned its agent children. The invocation
is now detached (#1481).
- An approved detached run with a null
workflowHashcan resume again (#1489). up --resume RUN_IDno longer demands a workflow path, matching its help text: the run’s stored workflow is relaunched (#1475).
Fallback chains recover instead of failing the run
A session-loss error from a provider (including Kimi’s “session is broken” banner) defeated the agent fallback chain: it silently skipped its Codex leads instead of retrying or falling through. A mixed-reason chain failure hard-failed the run instead of parking it.- Session-loss errors are now classified, so the chain retries or falls through to the next seat (#1480).
- A mixed agent-chain failure parks the run as
waiting-quotawith preflight context attached, resuming when capacity returns (#1482). - Codex preflight accepts valid ChatGPT auth even when
auth.jsonalso contains anOPENAI_API_KEY(#1447). - Both behaviors are pinned by new quota-chain-failover and session-loss-chain-failover e2e suites.
The Gateway finds your UI, and stays responsive while it does
bunx smthrs ui could fail three ways. The Gateway never registered workflows
launched by explicit path outside .smithers
(#1474). Registration
rendered every workflow synchronously, pegging the event loop so /health
went unreachable for minutes (GATEWAY_UNREACHABLE). And a custom-path
<UI path="/custom"> could 404 until background discovery reached it.
All three are fixed: explicit-path workflows register, <UI>/<TUI>
discovery is queued and drains one render per macrotask so /health answers
throughout, and an unmatched UI request drains pending discovery once before
resolving the mount.

The Monitor overview starts at All clear, a release-canary run is launched live, its running status pill appears in the sidebar, and the run detail shows the Healthy banner while the execution tree ticks through preflight, build, tests, and report to end on Completed with 4/4 tasks done.

Touring the finished run: the Completed banner, per-node durations, the node inspector with transcript and output, the Timeline, Debug, Frames, and XML views with a frame scrubber, and the Notable, All, and Activity event tabs.
A finished run tells the truth, and no agent outlives its engine
Three ways a run could look healthier than it was (#1464). A<Loop> or <ReviewLoop> that exits through the default
onMaxReached: "return-last" with its until predicate still false never
converged, yet the run reported done. The scheduler now records the loop as
exhausted (persisted on _smithers_ralph.exhausted, migration 0035), the
finished RunResult and the RunFinished event carry exhaustedLoops, and
the CLI stops calling it success:
running-healthy. When the full liveness
probe failed, status, ps, and inspect fell back to a path that ignored
the heartbeat entirely. The fallback now classifies from the run row alone
using heartbeat_at_ms and runtime_owner_id, and a continued run whose
segment never finished is treated as running so its stale heartbeat downgrades
the verdict instead of resolving to succeeded.
Agent subprocesses no longer survive their engine
(#1332). Agent CLIs spawn
detached as their own process-group leaders, the engine registers every agent
pid in _smithers_agent_processes (migration 0036), and any CLI invocation
sweeps that registry and group-kills entries whose engine is verifiably gone,
taking subagents, MCP servers, and tool children with it. cancel and down
sweep the runs they fence. Set SMITHERS_NO_ORPHAN_REAPER=1 to opt out of the
boot sweep.
ctx.outputs stops returning a plausible empty array
The callable formctx.outputs(outputs.probe) silently returned [] because
it indexed the snapshot with the raw argument. It now resolves through the
same table-ref resolution as every other accessor, and an argument that
resolves to no declared output table throws
(#1486).
- Regression tests pin the callable form to the same rows as the string name.
upgrade-dependents: open rename PRs downstream
The 0.33.0 rename left dependent repos importing a package that will never see another release. The newupgrade-dependents pack workflow discovers
open-source dependents (awesome-smithers, GitHub code search, and any
extraRepos you pass), then runs one lane per repo: an agent clones,
upgrades to smthrs, and must prove git grep returns zero hits before an
independent reviewer re-checks the diff and a draft PR is opened.
Per-run token usage in one query
“How many tokens did this run spend” used to mean replaying the event log and parsingTokenUsageReported payloads out of JSON. The engine now persists a
usage row per attempt (_smithers_run_usage, keyed by run, node, iteration,
and attempt) alongside each usage event it already emits, on both success and
failure paths. bunx smthrs usage --run RUN_ID prints the total, and
SmithersDb.getRunTokenUsage(runId) is a single SUM. Rows are upserts keyed
by attempt identity, so a provider that re-reports a cumulative running total
replaces its row instead of inflating the total, while genuine retries stay
separate attempts.
Durable agent checkpoints
Agents can now declarecheckpointCapabilities and persist a final
AgentCheckpoint per attempt. <Task id={B} fork={A}> starts B from a copy
of A’s final checkpoint when the target declares an exact codec, version,
and "fork" match, and falls back to the source attempt’s recorded forkable
conversation messages otherwise. Forking never mutates the source: multiple
tasks can fork the same source, and a forked task can itself be forked.
Nanocodex: a pinned-source sandboxed agent backend
NanocodexAgent connects Smithers to the external
smithers-nanocodex bridge:
a pinned stock Nanocodex agent run as a short-lived, checkpoint-aware worker
inside a Bubblewrap sandbox. The v0.0.1 integration is deliberately fixed
(one fresh serve worker and one stock Nanocodex turn per generate() call,
no daemon or worker pool) and fails closed when the sandbox requirements are
not met. Linux x64 only; see the
Nanocodex integration page for the requirements.
A theme registry for the UI kit
@smthrs/ui-styleguide now ships a generated theme registry with eight
palettes: Catppuccin, Fucory, GitHub, Gruvbox, Night Owl (new in this
release), One, Rosé Pine, and Solarized. A generator script keeps every
palette’s light and dark variants, contrast ratios, and CSS custom properties
in sync, so run UIs built on smthrs/ui pick up a coherent theme with one
setting.
stereOS in the browser, and three new sites
- custom-sandbox.smithers.sh, published at
the time as
stereos.smithers.sh, ran the stereOS demo entirely in your browser inside a WebContainer, so the sandbox-provider demo needed no local checkout. - patterns.smithers.sh is a field guide to orchestration patterns.
- research.smithers.sh hosts the persuasion-gap paper.
- status.smithers.sh gained Smithers Code and Smithers Cloud API components, each verified live before listing, and a missing feed now answers a real 404 instead of an HTML page labelled as status.
Other improvements
- The bug-triage-train workflow ships in the pack with a live UI: file a batch of bug reports through triage, reproduction, and fix lanes.
CodexAgentemits--sandbox workspace-writenow that codex-cli 0.147 removed--full-auto, so full-auto-configured workflows keep launching on current codex installs.- Queued steers expire inside the terminal cancel transaction, and steer enqueue is fenced against terminal runs, so an instruction can no longer land on a run that is already ending.
- Detached built-in runs resume correctly after approval decisions.
- A Codex agent configured through the array form keeps its reasoning effort instead of silently dropping it.
bunx smthrs tail --format jsonlemits raw event JSON per line and nothing else, so scripted consumers stop parsing banner noise.bunx smthrs bug --run RUN_IDattaches the run’s lifecycle events instead of a ~25 second window of TaskHeartbeat noise, and generates a correct report title (#1484).- Panel moderators inside a Ralph loop no longer deadlock over Ralph’s iteration-suffixed dependency names (#1487).
bunx smthrs hijackre-emits the agent’s launch flags.--resumerestores conversation state only, so a hijacked session lost the model, permission mode, and config dir the workflow agent ran with. Those are recorded on the hand-off now and replayed onto the CLI invocation.reconstructUnifiedDiffbounds its LCS grid by bytes instead of cells. The oldn * mcell guard allocated one typed array per old line, so a lopsided payload passed the check and still allocated about a gigabyte; one flatUint32Arraymakes the cell bound a real byte bound.- The live node diff path enforces the payload cap it previously bypassed, so
an oversized working-copy diff raises
DiffTooLargeinstead of serializing uncapped. - The gateway UI keys its live diff refresh on the last event’s seq rather than the event ring’s length, which stops changing once the 200-event ring fills and froze the live diff on long runs.
- The review workflow UI renders reviewer feedback, synthesis, and issue
descriptions as formatted Markdown through the shared Markdown primitive
instead of one raw paragraph, and a mid-run rejection no longer shows a
final
Blockedbadge next toRUNNING: verdicts only go final once the run is terminal. EmptyStateandSectionHeaderaccept element titles again. Both extendedComponentProps<"div">, whose intrinsictitle?: stringintersected with their owntitle?: ReactNodeslot and collapsed it tostring & ReactNode.scheduleranddevtoolsre-emitted their stale committed declarations fromtsup --dts-only, which madecheck:dtsfalse-green on type changes. Both configs now clean the emittedindex.d.tsfirst.- Hermes and OpenClaw are listed in the README harness table and the npm package description alongside the other CLI harnesses.
- The detached launch admission wait is configurable, and a live engine child
is no longer killed at the deadline.
bunx smthrs up -dwaited a fixed 30 seconds for the engine child to prove it persisted the run row, which a heavily loaded machine can miss on module parse alone; the launcher then terminated the healthy child and reported an empty log. A child that is still alive at the deadline now gets a grace window of four times the timeout, with a progress note on stderr, and the final failure names the live pid instead of “(empty)”. SetSMITHERS_DETACHED_ADMISSION_TIMEOUT_MSto raise or lower the base window. The default is still 30 seconds, and a value below 1000ms falls back to it. - The Trellis benchmark lab pin now carries the validated v2 authoring ladder: kit-only scripts, static validation, fuel accounting, evidence-bound settlement, mission CLI, and hardened batched ARC evaluation planning. It is internal benchmarking infrastructure, not part of any published package.
- Test scratch directories now go through a tracked cleanup helper, preventing
abandoned SQLite, PGlite, sandbox, and workflow workspaces from accumulating
under
$TMPDIRafter suites finish or throw. - The TUI node inspector’s empty Logs tab again renders the parenthesized
(no log events for this node)placeholder, with a headless regression test that runs even when the optional zmux PTY suite is unavailable. - Claude Code OAuth credentials stored in the macOS Keychain are found
everywhere they matter.
bunx smthrs usageand the agent availability probe now read the per-config-dir Keychain item for isolated accounts (Claude Code-credentials-<hash of the config dir>), with no fallback to the default install’s item, so a validly logged-in account stops reportingNO_USABLE_AGENTSwhen theclaude auth statusprobe times out, and one account’s token is never attributed to another. <Task fork>authored inside a Subflow or Sandbox child workflow keeps its fork edge; the dom extraction variant used for child workflows dropped the prop and the fork was silently lost.- Asking oneshot for a codex agent while the codex-paused marker is set now
names the pause itself, its until/reason, and how to clear it
(
SMITHERS_CODEX_PAUSED=0or delete the marker) instead of the generic “not enough availability signals” that pointed at healthy auth. - effect and the
@effect/*packages are aligned at 4.0.0-beta.105. Keeping the family on one beta prevents npm from satisfying caret peer ranges with multiple effect runtimes in an end-user install. - The
smthrsumbrella is guarded against type-vs-runtime export drift: a new test imports every documented agent export and asserts it exists at runtime, the exact shape of the bug wherefallbackAgentstypechecked but threw “Export named ‘fallbackAgents’ not found” on import. The facade’s committedindex.d.tswas regenerated against the release base. - The gateway PTY resize test polls for the new terminal geometry instead of
reading it once after a fixed half-second sleep, so a loaded CI runner no
longer reds the
packages/servershard over a resize frame that merely arrived late.
Upgrade notes
- No breaking changes at runtime. All 0.33.0 rename guidance still applies.
- If a package manifest overrides Effect prereleases, update the whole
effectand@effect/*family to beta.105 together. AgentCapabilityRegistrygains afileChanges: { supportsFileChanges, supportsUnifiedDiff }field. Registries are normalized with both flagsfalsewhen absent, so existing agents keep working; a custom agent typed against the exported TypeScript type needs the field added.getRunDiffandgetNodeDiffreturn a diff for a non-terminal run now instead of refusing. Bundles computed from a live working copy carrylive: true; treat the terminal bundle as the authoritative snapshot.- A mixed agent-chain failure now parks the run as
waiting-quotainstead of failing it. Checkbunx smthrs psfor parked runs before concluding a run died. bunx smthrs up --resume RUN_IDwithout a workflow path is now valid; the stored workflow is relaunched.- This release adds several store migrations: the
_smithers_attempts.effortcolumn, the_smithers_steerstable,_smithers_ralph.exhausted, the_smithers_agent_processestable, agent checkpoints, the_smithers_run_usagetable, and the Herdr supervision tables. They apply on first open, so no manual step is needed, but an older CLI pointed at a migrated database will not read the new columns. - The herdr client pins wire protocol 19 (herdr 0.8.0). Explicit
bunx smthrs herdrcommands against another protocol fail closed withHERDR_PROTOCOL_MISMATCHbefore any mutating call; the optional mirror paths log and continue. herdr remains fully optional. CodexAgentmaps full-auto to--sandbox workspace-writeinstead of the removed--full-autoflag. codex-cli releases that predate--sandboxneed an upgrade before Codex tasks will launch.- 0.33.1 was tagged but never published to npm. If you tracked its changelog, every entry ships in 0.34.0; there is no npm artifact to migrate through.
bunx smthrs statuscan now return the verdictdegradedfor a finished run whose loop never satisfied itsuntilcondition. The run rowstatusstaysfinishedandRunStatestayssucceeded, so scripts that key off the run status are unaffected; scripts that assertverdict === "done"should acceptdegradedor treat it as the non-convergence signal it is.- Every CLI invocation now sweeps
_smithers_agent_processesand group-kills agent processes whose engine is gone. SetSMITHERS_NO_ORPHAN_REAPER=1if you deliberately keep agent processes alive past their engine.
The full changelog
254 commits since 0.33.0, plus this changelog update, touching 901 files: 127,655 insertions, 6,718 deletions. Much of that insertion count is regeneratedllms-*.txt docs bundles; excluding those, the lockfiles, and
declaration bundles it is 839 files, 91,632 insertions, 3,524 deletions. The
complete commit-level list is in
CHANGELOG.md.
Hit a bug? bunx smthrs bug files a report in seconds, and --run RUN_ID
attaches the run’s events for you.