@crow-agent/session-backend-sqlite-node
v0.2.0
Published
SQLite session storage for the Crow agent runtime on Node.js.
Maintainers
Readme
@crow-agent/session-backend-sqlite-node
Durable session storage for @crow-agent/agent-core, built on Node's built-in
node:sqlite. Point it at a directory and it persists sessions, branches,
and operation lanes as SQLite databases — one file per session by default.
import { BACKGROUND_CONTEXT } from "@crow-agent/agent-core";
import {
createNodeSqliteFactory,
SqliteSessionRepo,
} from "@crow-agent/session-backend-sqlite-node";
const factory = createNodeSqliteFactory();
const repo = new SqliteSessionRepo({
databaseFactory: factory,
directory: "/var/lib/crow/sessions",
});
const session = await repo.create({ id: "demo" }, BACKGROUND_CONTEXT);
const main = await session.createBranch("main", null, BACKGROUND_CONTEXT);
await main.appendMessage(
{ role: "user", content: "hi", timestamp: Date.now() },
BACKGROUND_CONTEXT,
);
await session.close(BACKGROUND_CONTEXT);
await repo.close(BACKGROUND_CONTEXT);Where sessions live
By default every session gets its own file under directory. Session ids
made only of ASCII letters, digits, _, and - map straight to
{sessionId}.sqlite; anything else is encoded as ~-prefixed base64url over
its UTF-16 code units, so a hostile id can never escape the repository
directory. The durable session id itself is never altered, and every metadata
object the repository hands back carries the canonical on-disk path. If you'd
rather keep many sessions in one file, pass databasePath instead — its
parent directory is created on demand.
Opening, listing, deleting
The database factory treats the three open modes as separate operations:
create, open read-write without creating, and open read-only without
creating. open() and delete() validate that the metadata resolves inside
this repository and never materialize a database that isn't there. list() is
read-only and forgiving — corrupt files, newer storage versions, and foreign
databases are skipped rather than reported. Forking is the deliberate
exception: it accepts a source path outside the repository, opening exactly
that container read-only instead of substituting a live local session that
happens to share the id.
One writer per session
Write ownership belongs to the host, not to this backend: opening the same session for writes from two processes is unsupported. The repository does reject overlapping local create/open/fork/delete claims on one id, but there is no cross-process lease, lock, fence, heartbeat, or takeover — shut the worker down before you delete its sessions.
When the fork source is open in the same repository, the snapshot is queued
behind that source's commit queue. Any other source — including one a live
worker currently holds — is read through its own read-only connection inside
a single deferred WAL transaction, so the worker's later commits can land
while the snapshot stays open. In a shared container, deleting a session
removes only that session's rows. Repository close() waits for every open
session's cleanup attempt before surfacing failures. There is no search
service or full-text index here; search belongs in a separate projection.
