<Memory> system with durable XState folds, a shared cross-app UI
library, Microsandbox support, and broad security and durability hardening.
Across the full v0.28.0..HEAD range that is 225 commits, 726 files changed,
84,604 insertions and 7,564 deletions, including 52 feature commits, 70 bug-fix
commits, 4 refactors, 22 test commits, and 47 docs commits.
Smithers 0.29.0: first-class memory, XState folded over durable rows, an open source microVM sandbox provider, and a shared UI component library.
Upgrading
First-class memory
Smithers 0.29.0 introduces a declarative<Memory> component. Wrap any part
of a workflow in a <Memory> provider and every task inside it can recall
relevant context before it runs, expose remember/recall tools to its agent while it runs,
and retain a digest of what it produced after it finishes: durably, across
runs, in a Hindsight memory bank stored in
your own Postgres.
bank/banks,tags,recall("auto", a fixed query, orfalse),budget,maxTokens,primers,retain, andtools: inherited by descendant tasks through context, overridable per task via the existingmemoryprop.- Recall is scoped per bank, so distinct banks keep distinct recall scopes instead of bleeding into each other.
- Every injected block and remembered tag is capped by an explicit bound, so recall never blows past your token budget.
- Set
HINDSIGHT_URL(plus optionalHINDSIGHT_API_KEY) to activate the Hindsight-backed store. Without it, the existing local SQLite facts store and behavior are unchanged.
memory prop:
XState as durable derived state
smthrs/xstate adds an optional XState v5 integration for
workflows whose control flow has outgrown a set of hand-written conditionals.
It does not persist an actor or introduce a second scheduler. Instead,
useSmithersMachine folds a machine over the output, approval, timeout, and
signal rows Smithers already persists. Resume, fork, and rewind therefore
reconstruct the machine from the same durable history as the rest of the run.
- Event sources read typed durable rows and order them on the shared provenance sequence, with deterministic declaration-order tie-breaking.
- Signal sources support
correlationId, so concurrent listeners using the same signal name do not consume one another’s history. - A mount-time lint rejects actor-only and side-effecting machine features
such as
invoke,spawn,after, and custom actions, and detects constant task identities in re-enterable states before they can deadlock a run. - A reducer-content hash and run-scoped, bounded prefix cache make incremental folds fast without serving a snapshot produced by another run, fork, or machine definition.
xstateis an optional peer. Projects that never import the integration do not need it installed, and the main facade remains isolated from it.
A shared UI component library
If you have built a custom workflow UI, you have probably rebuilt a terminal, a diff view, or a markdown renderer more than once. 0.29.0 ships a shared component library acrosssmthrs/ui and
smthrs/gateway-ui so you do not have to.
From smthrs/ui directly:
- A dependency-free Markdown renderer that renders through React
children, never
innerHTML, so it is XSS-safe by construction. Links with an unsafe scheme render without anhrefand never reach youronLinkClickhandler. - A dependency-free DiffHunks component and diff domain, for when you do not need the full diff adapter.
- FileTree, CollapsiblePanel, and StageStrip components.
ui barrel. Import each from its own path:
- Terminal over xterm.js, from
smthrs/ui/adapters/terminal. - MarkdownEditor over Milkdown Crepe, from
smthrs/ui/adapters/markdown-editor. - PierreDiffView over
@pierre/diffs, fromsmthrs/ui/adapters/pierre-diff-view.
smthrs/gateway-ui: a ReactFlow and dagre powered
WorkflowGraph DAG canvas, and a run-aware NodeOutputCard.
smithers-ui synced its design tokens to
design system v2 and rendered the workspace picker mark. The one behavior
change worth knowing: status colors were realigned to the new semantics.
Running now reads as brand color, waiting and paused read as amber, and
queued and pending read as muted. If you built a custom UI against the old
status colors, check it against the new ones.
Microsandbox: a first-class sandbox provider
packages/microsandbox adds a SandboxProvider backed by the open source
Microsandbox microVM SDK.
Each run boots a local microVM with its own kernel, writes the sandbox
request through the host-to-guest filesystem channel, and reads the result
back the same way. No daemon or hosted sandbox service is required.
One microVM per run: the sandbox request crosses the host-to-guest filesystem channel into a guest with its own Linux kernel, and the result comes back the same way.
microsandbox, exported as MICROSANDBOX_PROVIDER_ID.
The Freestyle example provider is retired in its favor. If you were using
it, switch to Microsandbox or one of the other first-class providers
(Vercel, AWS, GCP, Daytona).
See the Microsandbox provider docs.
Durable signal and output rows
Workflow contexts can now read durable signal rows directly, and the driver exposes durable typed output rows backed by a new output-provenance persistence layer inpackages/db. Together they give you durable state you
can query mid-run instead of threading it back through task outputs.
seq, so output
and signal history can be interleaved into one causal order.
One fix ships alongside: output provenance metadata is no longer included in
output payloads returned to consumers. The provenance columns are internal
bookkeeping, and they used to leak into every payload; they are stripped
before the payload reaches you.
Security and durability hardening
Most of this release went where you will not see a single feature callout: 32 emoji-prefixed bug-fix commits and 6 test commits closing gaps that only show up under real load or a misbehaving client.Where the 213 commits went: 67 fixes and 22 tests against 48 features. Reliability is the feature.
- An empty
bind={[]}provenance binding is parked as missing instead of dispatching an unproven task. - Real-process test admission rejects impostor runner scripts instead of trusting an executable path alone.
maxResponseBodyBytes, default 1 MiB, except the transcription tool’s remote
audio download which keeps its 25 MiB default), plus Gateway WebSocket run-event
subscription caps at every scope (gateway 256, per-user 32, per-connection 8,
per-run 64). Alongside those:
- Concurrent resume paths (timer, signal, approval, webhook, operator) are deduplicated through a runId-keyed in-flight gate, and run-tracking cleanup no longer lets an older settling invocation wipe a newer invocation’s records.
- Cron fires are now claimed via compare-and-set before launching, so a scheduler cannot double-fire the same scheduled run.
- The driver strips the persisted
runIdcolumn from table-shapedctx.input, so it no longer trips an unrecognized-key error in a strict Zod input schema. - The standalone Telegram client clamps
retry_afterto a documented cap (default 30s), races the retry wait against the call’sAbortSignal, and no longer treats an aborted body read as a retryable error that could trigger a duplicate request. - Claude subscription accounts with organization-level overage purchasing disabled no longer have every agent invocation misclassified as a quota rejection.
Other improvements
-
The driver gained an
onErrorhook so external error reporters, Sentry for example, can capture run failures. It fires once per node or run failure occurrence, and the report names which one: -
Eval suites support LLM-judge assertions, for cases where correctness is
about meaning rather than an exact JSON match:
judge.thresholddefaults to0.8. A case with bothexpectedandjudgepasses only when every deterministic assertion passes and the judge score meets its threshold. - The observability package supports OTLP authentication headers for authenticated trace export.
-
Detached (
up -d) run logs now default to<workspace>/.smithers/logs/<runId>.logwith automatic retention-window and total-size-capped cleanup, instead of accumulating indefinitely in the workflow source directory. One workspace had 711 immortal log files totaling 1.4 GB before this fix. -
bunx smthrs uino longer silently opens a different workflow’s UI when a long-lived gateway’s registry predates a newly authored workflow: the gateway now rescans on a registry miss, and the CLI fails loud with an actionable error instead of guessing. -
The CLI and gateway now tolerate leftover jj merge-conflict markers in the
workspace
package.jsoninstead of crashing on JSON parse; the gateway boots read-only in that state. -
The db package now probes output tables via
sqlite_masteron sqlite-dialect external backends, fixing a table-detection bug. -
A new Workflow testing framework guide
documents the expanded
@smthrs/testingsuites: cutpoint-conformance, replay-identity, and artifact-parity. - A new example workflow, “Daily CEO Intel” (branded in its own docs as “The Smithers Signal”), demonstrates a durable daily-intelligence-brief pipeline with multi-provider SDK-agent fallback (Anthropic to OpenAI to Gemini) and Cloudflare KV and R2 publishing. It ships marked as an example in the workflow pack, not a supported product feature.
Upgrade notes
- npm upgrades directly from 0.28.0. Upgrading to 0.29.0 picks up the
complete release described above, headlined by
<Memory>. - Memory bypasses the output cache when active. Tasks wrapped in an
active bank-based
<Memory>config bypass the durable output cache, because recalled context is a mutable input that is not part of the cache identity. Legacy namespace or object-form memory metadata remains inert and keeps its previous cache semantics. HindsightMemoryStoreassumes a single writer per contract store. This constraint and its invariant tests are documented in the memory guides.- Wait-for-event tagged results are an explicit opt-in. Existing waits
keep receiving the raw signal payload unchanged. A timeout continuation
now requires the tagged envelope instead of writing an ambiguous
null. - Detached run logs moved.
up -dlogs now default to<workspace>/.smithers/logs/<runId>.loginstead of the workflow source directory. An explicit--log-dirstill overrides, andSMITHERS_LOG_RETENTION_DAYS/SMITHERS_LOG_MAX_TOTAL_BYTEStune the automatic cleanup. - Tool response bodies are capped by default. Generated OpenAPI tools and
the built-in HTTP tool now stop reading a response body at 1 MiB. The
transcription tool’s remote audio download keeps its 25 MiB default. Raise
or lower either with
maxResponseBodyBytes. A call that previously streamed a larger body now fails with an explicit limit error instead of buffering without bound. - The Freestyle sandbox provider was removed. It shipped as an example provider and Microsandbox replaces it. Switch to Microsandbox or one of the other first-class providers (Vercel, AWS, GCP, Daytona).
StatusClassgained arunmember andstatusClass()re-buckets. Running now maps to the brand class, and waiting, paused, queued and pending map to their new classes. An exhaustiveswitchor lookup map overStatusClasswill fail to compile until you handlerun.
The full changelog
The complete commit-level history for this release is in CHANGELOG.md on GitHub. Found a bug? Runbunx smthrs bug to file it.