@b4run/sqlite-storage
Use this whenCopy link to section: Use this when
Use this Node-only package for local, single-process checkpoint and Agent Protocol thread persistence. It is not encrypted and not shared across replicas.
Install and importCopy link to section: Install and import
npm install @b4run/sqlite-storageimport { createThreadsStore, sqliteCheckpointer } from "@b4run/sqlite-storage"Compatibility and audienceCopy link to section: Compatibility and audience
| Surface | Runtime | Purity | Audience | Stability |
|---|---|---|---|---|
@b4run/sqlite-storage | node-only | not-claimed | application | supported |
The package uses Node's node:sqlite DatabaseSync and LangGraph checkpoint contracts.
Public exportsCopy link to section: Public exports
@b4run/sqlite-storageCopy link to section: @b4run/sqlite-storage
| Export | Responsibility |
|---|---|
SqliteCheckpointerOptions | Select the checkpoint database path. |
B4SqliteSaver | Implement the LangGraph checkpoint saver methods. |
sqliteCheckpointer | Open, migrate, and return a checkpoint saver. |
CreateThreadInput | Supply an optional thread ID and metadata. |
Thread | Describe persisted thread identity, timestamps, metadata, and status. |
ThreadStatus | Restrict status to idle, busy, or interrupted. |
ThreadsStore | Define thread create/read/delete/list/update operations. |
ThreadsStoreOptions | Select the thread database path. |
createThreadsStore | Open, migrate, and return a thread store. |
InterruptGrantRecord | Describe a stored approval-grant row. |
InterruptGrantStore | Define the approval-grant store contract. |
InterruptGrantConsumption | Describe a conditional consume's outcome. |
InterruptGrantStoreOptions | Select the approval-grant database path. |
createInterruptGrantStore | Open, migrate, and return an approval-grant store. |
ClientToolCallRecord | Describe a stored client tool call row. |
ClientToolCallAnswer | Describe an answer attempt's outcome. |
ClientToolCallStore | Define the client tool call store contract. |
ClientToolCallStoreOptions | Select the client tool call database path. |
createClientToolCallStore | Open, migrate, and return a client tool call store. |
openWorkspaceInstallation | Own a local installation's durable source and association storage. |
WorkspaceInstallation | Hold installation identity, guarded stores, per-thread sandbox records, staged workspace sources, and close. |
openWorkspaceInstallationReader | Open a read-only view of an installation another process owns, without its admission lock. |
WorkspaceInstallationReader | Hold installation identity, read-only association lookups, and close. |
WorkspaceSourceStore | Persist and retrieve verified source bundles. |
WorkspaceStagedSourceStore | Keep uploaded sources with their file paths and uploaders, and each thread's staged reference, and reclaim sources nothing references. |
WorkspaceStagedSourceError | Refuse a staging request by code: not_held, already_staged, quota_exceeded, or uploader_invalid. |
WorkspaceAssociation | Describe creating, ready, deleting, or deleted state with its revision. |
WorkspaceAssociationStore | Insert source-backed intents, with an optional thread sandbox record in the same transaction, and transition associations by revision. |
WorkspaceThreadSandboxStore | Read the sandbox record a thread's first admission wrote, and keep that thread's "Always" grants. |
Key contractsCopy link to section: Key contracts
export interface SqliteCheckpointerOptions {
readonly path: string
}export declare function sqliteCheckpointer(options: SqliteCheckpointerOptions): B4SqliteSaverexport type ThreadStatus = "idle" | "busy" | "interrupted"export interface Thread {
readonly thread_id: string
readonly created_at: string
readonly updated_at: string
readonly metadata: Record<string, unknown>
readonly status: ThreadStatus
}export interface CreateThreadInput {
readonly thread_id?: string
readonly metadata?: Record<string, unknown>
}export interface ThreadsStore {
createThread(input: CreateThreadInput): Promise<Thread>
getThread(threadId: string): Promise<Thread | undefined>
deleteThread(threadId: string): Promise<void>
listThreads(): Promise<Thread[]>
updateStatus(threadId: string, status: ThreadStatus): Promise<void>
updateMetadata(threadId: string, patch: Record<string, unknown>): Promise<void>
}export interface ThreadsStoreOptions {
readonly path: string
}export declare function createThreadsStore(
options: ThreadsStoreOptions,
): import("./store.js").ThreadsStoreBehavior contractsCopy link to section: Behavior contracts
Behavior contract sqlite.checkpointer.persistence
A file-backed SQLite checkpoint persists across saver instances.
Behavior contract sqlite.threads.order
listThreads returns most-recently-updated threads first.
Thread ordering caveat
Ordering is updated_at DESC and does not define a tie-break for equal timestamps.
Behavior contract sqlite.db.pragmas
SQLite opens with WAL mode, foreign keys enabled, and synchronous NORMAL.
Database mode caveat
WAL applies only to file-backed databases. :memory: skips the WAL statement.
Behavior contract sqlite.public.no-close
The public SQLite saver and thread store expose no explicit close method.
Database lifecycle and query caveats
Each factory opens and retains a DatabaseSync handle. The public surface does not close automatically and has no cleanup hook, so treat these as process-lifetime stores. Use separate files (such as checkpoints.sqlite and threads.sqlite), because both components use a global schema_version table and must not share one path.
Saver list() orders lexically by checkpoint ID, ignores the filter option, and returns lightweight tuples without pending writes. Fetch a specific tuple to get its pending writes. Thread status and metadata updates for a missing ID are no-ops. Metadata updates shallow-merge.
Workspace installationsCopy link to section: Workspace installations
openWorkspaceInstallation(appRoot) owns .b4/workspaces/state.sqlite and a
separate admission database. Only one active owner on one local host may manage
an installation. Separate state transactions permit concurrent threads. Close
the owner after execution and provider cleanup settle. Interrupted initialization
can resume. Missing established state or mismatched identity fails closed.
Network filesystems, multiple hosts, and whole-state-directory replacement are
unsupported. The runtime normally owns this connection for the application.
Associations require retained source before insertion. Readiness is validated against the intent and uses an expected revision. Deleting state prevents new admission and stale readiness publication. Deleted rows remain tombstones.
openWorkspaceInstallationReader(appRoot) opens the same two databases
read-only, takes no admission transaction, and so coexists with the live owner.
It validates what the owner validates and requires a ready installation. It
never creates the directory or either database, so a missing installation is an
error rather than an empty view. It exposes associations.get and
associations.list only. A co-located host process uses it to resolve a
thread's published ReadyWorkspace for @b4run/cli/workspace's
withManagedWorkspaceReader.
WorkspaceInstallation.staged serves sandbox.stagedWorkspaces. An upload is
kept in the same content store, keyed by its own digest, with its file paths,
size and uploaders; a re-upload rewrites nothing and refreshes its upload time.
Only an uploaded source is stageable: a source an admission stored is not, even
though its bytes are in the same table. A thread's staged reference is recorded
in the same database, refused for a source that was not uploaded. reclaim deletes every source no association, staged thread or caller
reference names, uploads only once older than the retention window, in one
transaction. The read-only reader neither needs nor creates these tables.
Cleanup errors retain deletion state so a later attempt can finish it.
Examples and related guidesCopy link to section: Examples and related guides
import { createThreadsStore, sqliteCheckpointer } from "@b4run/sqlite-storage"
export const checkpointer = sqliteCheckpointer({ path: ".b4/checkpoints.sqlite" })
export const threadsStore = createThreadsStore({ path: ".b4/threads.sqlite" })Continue with Persistence and Tenancy and CLI API.