createSmithers and friends) owns that store for you and
hands back a typed db handle, so you almost never touch this layer directly.
This page is the escape hatch. Reach for it when you build a custom backend,
inspect a store outside a running workflow, or generate tables from Zod schemas
yourself.
SmithersDb is also re-exported from @smithers-orchestrator/db/adapter for
callers that pin the subpath; the facade export above is the supported one.SmithersDb
The adapter. It wraps a Drizzle database handle (abun:sqlite database, or a
Postgres/PGlite connection descriptor) and exposes every persistence operation
the engine needs. The factory constructs one and the scheduler drives it; you
construct one yourself only for a custom control plane.
The underlying Drizzle handle. SQLite uses Drizzle queries directly; the
Postgres/PGlite descriptor routes through the internal SQL message storage.
insertRun, updateRun, getRun, listRuns, heartbeatRun,
requestRunCancel, requestRunHijack, claimRunForResume,
listStaleRunningRuns, and the ancestry helpers track a run from start to
terminal state and support crash recovery.insertNode / getNode / listNodes and insertAttempt / updateAttempt /
heartbeatAttempt record per-node execution and each retry attempt.upsertOutputRow, deleteOutputRow, getRawNodeOutput, and hasPhysicalTable
read and write the typed output tables a workflow’s schemas produce. Keyed by
runId / nodeId / iteration.insertFrame, listFrameChainDesc, and reconstructFrameXml persist and
replay the delta-encoded UI frame stream (with an in-memory LRU XML cache).withTransaction / withTransactionEffect run a write group atomically.
read(label, op) and write(label, op) wrap a single operation as a runnable
Effect with metrics and SQLite write-retry. SQLite serializes writes per
client; Postgres uses real transactions.Read-only escape hatch. Accepts only
SELECT / WITH / EXPLAIN / VALUES;
any DDL or mutation keyword is rejected before execution. Use it for
introspection, never for writes.The agent-message and event-history store behind the adapter, created from the
same handle. See
ensureSmithersTables.RunnableEffect: an Effect you can yield* inside an
Effect program, or await directly as a promise.
Source adapter.js · adapter/SmithersDb.js · Tests db-adapter.test.js · See also How it works, SchemaRegistryEntry
loadOutputs / loadOutputsEffect
Read every persisted output row for a run, across all of a workflow’s output tables, in one call. Given thetables map the factory returns and a RUN_ID,
it returns a snapshot keyed by both the schema name and the physical table name.
Boolean columns are coerced back to JS booleans; the reserved input table is
skipped.
The same handle the adapter wraps. Dialect-aware: Drizzle for SQLite, a
parameterized
$1 query for the Postgres descriptor.Map of output name to its Drizzle table (the
tables value from the factory).
Entries without a runId column, or the input entry, are skipped.The run to read. Placeholder:
RUN_ID.An object whose keys are both the schema name and the snake_case table name;
each value is the array of Drizzle-shaped rows (camelCase keys) for that run.
loadOutputsEffect is the same query returning an
Effect.Effect<OutputSnapshot, SmithersError> instead of a promise. Use it
inside an Effect pipeline; use loadOutputs for a one-off read.
snapshot.js · Tests db-snapshot-load.test.js · See also db-output-roundtrip.test.js
ensureSmithersTables
Create the core Smithers tables (the internal message and event-history storage the adapter depends on) on a fresh handle, if they do not already exist. Safe to call on every boot.The Drizzle handle to initialize. Runs synchronously for SQLite.
For a Postgres connection descriptor this synchronous helper is a no-op:
Postgres schema setup is asynchronous and the Postgres/PGlite entry points
ensure the schema (awaited) before the engine starts. Output tables are created
separately by
syncZodTableSchema.ensure.js · Tests db-ensure.test.js
Zod -> table helpers
Turn a Zod object schema into a SQLite output table. Each table gets the fixedrun_id / node_id / iteration prefix and a composite primary key; field keys
are snake-cased and mapped to columns by Zod type (string/enum/literal -> TEXT,
z.number() / float -> REAL (fractions are preserved), z.int() -> INTEGER,
boolean -> INTEGER boolean mode, and arrays/objects/unions -> JSON TEXT). Pass
opts.isInput for the single-PK input-table shape. Output field names may not
reuse the reserved key columns runId/nodeId/iteration (input reserves only
runId); a collision throws INVALID_INPUT at construction.
Build a Drizzle
sqliteTable from the schema. This is what the factory calls
to materialize tables.Emit
CREATE TABLE IF NOT EXISTS .... Pass opts.dialect (default "sqlite")
to emit Postgres-compatible column types instead.Create the table and reconcile a drifted one: it runs the
CREATE TABLE, then
ALTER TABLE ADD COLUMN for any field missing from an older table, and records
column kinds in _smithers_output_schema_columns. Idempotent. sqlite must be
a bun:sqlite database (or compatible .run / .query handle).The user-defined columns derived from the schema, excluding the fixed prefix.
Each entry carries the snake_case
name, the SQLite type, and the logical
kind (string / number / boolean / json).zodToTable.js · zodToCreateTableSQL.js · Tests zod-to-table-unit.test.js · zod-to-sql.test.js
Utilities
Small building blocks the helpers above are made of, exported for the same custom-backend cases.Convert a
camelCase field name to snake_case (the column-naming rule used
throughout). "createdAtMs" -> "created_at_ms".Strip the
optional / nullable / default wrappers off a Zod type to reach
its base type, so column mapping sees the underlying kind.Render a Zod object schema as a pretty-printed JSON example string: field
descriptions become string placeholders, numbers
0, booleans false, arrays
a one-element sample, and the first enum value is used. Handy for prompting an
agent with the exact output shape it must return.utils/camelToSnake.js · unwrapZodType.js · zod-to-example.js · Tests camel-to-snake.test.js · unwrap-zod-type.test.js · See also Authoring API, Types reference