@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

bash
npm install @b4run/sqlite-storage
ts
import { createThreadsStore, sqliteCheckpointer } from "@b4run/sqlite-storage"

Compatibility and audienceCopy link to section: Compatibility and audience

SurfaceRuntimePurityAudienceStability
@b4run/sqlite-storagenode-onlynot-claimedapplicationsupported

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

ExportResponsibility
SqliteCheckpointerOptionsSelect the checkpoint database path.
B4SqliteSaverImplement the LangGraph checkpoint saver methods.
sqliteCheckpointerOpen, migrate, and return a checkpoint saver.
CreateThreadInputSupply an optional thread ID and metadata.
ThreadDescribe persisted thread identity, timestamps, metadata, and status.
ThreadStatusRestrict status to idle, busy, or interrupted.
ThreadsStoreDefine thread create/read/delete/list/update operations.
ThreadsStoreOptionsSelect the thread database path.
createThreadsStoreOpen, migrate, and return a thread store.
InterruptGrantRecordDescribe a stored approval-grant row.
InterruptGrantStoreDefine the approval-grant store contract.
InterruptGrantConsumptionDescribe a conditional consume's outcome.
InterruptGrantStoreOptionsSelect the approval-grant database path.
createInterruptGrantStoreOpen, migrate, and return an approval-grant store.
ClientToolCallRecordDescribe a stored client tool call row.
ClientToolCallAnswerDescribe an answer attempt's outcome.
ClientToolCallStoreDefine the client tool call store contract.
ClientToolCallStoreOptionsSelect the client tool call database path.
createClientToolCallStoreOpen, migrate, and return a client tool call store.
openWorkspaceInstallationOwn a local installation's durable source and association storage.
WorkspaceInstallationHold installation identity, guarded stores, per-thread sandbox records, staged workspace sources, and close.
openWorkspaceInstallationReaderOpen a read-only view of an installation another process owns, without its admission lock.
WorkspaceInstallationReaderHold installation identity, read-only association lookups, and close.
WorkspaceSourceStorePersist and retrieve verified source bundles.
WorkspaceStagedSourceStoreKeep uploaded sources with their file paths and uploaders, and each thread's staged reference, and reclaim sources nothing references.
WorkspaceStagedSourceErrorRefuse a staging request by code: not_held, already_staged, quota_exceeded, or uploader_invalid.
WorkspaceAssociationDescribe creating, ready, deleting, or deleted state with its revision.
WorkspaceAssociationStoreInsert source-backed intents, with an optional thread sandbox record in the same transaction, and transition associations by revision.
WorkspaceThreadSandboxStoreRead the sandbox record a thread's first admission wrote, and keep that thread's "Always" grants.

Key contractsCopy link to section: Key contracts

ts
export interface SqliteCheckpointerOptions {
  readonly path: string
}
ts
export declare function sqliteCheckpointer(options: SqliteCheckpointerOptions): B4SqliteSaver
ts
export type ThreadStatus = "idle" | "busy" | "interrupted"
ts
export interface Thread {
  readonly thread_id: string
  readonly created_at: string
  readonly updated_at: string
  readonly metadata: Record<string, unknown>
  readonly status: ThreadStatus
}
ts
export interface CreateThreadInput {
  readonly thread_id?: string
  readonly metadata?: Record<string, unknown>
}
ts
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>
}
ts
export interface ThreadsStoreOptions {
  readonly path: string
}
ts
export declare function createThreadsStore(
  options: ThreadsStoreOptions,
): import("./store.js").ThreadsStore

Behavior 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.

ts
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.