TypeScript API: Storage and memory
Durable threads (SQLite, Postgres, in memory), encryption, audit, retention and long-term memory. Generated from the declarations and doc comments of criogenai 0.1.7. See also: TypeScript API overview, the same module in Python.
import {
Storage,
Memory,
StorageOptions,
MemoryOptions,
RetentionOptions,
} from "criogenai";Contents#
| Name | Kind | Summary |
|---|---|---|
Storage | class | Where threads, long-term memory and the audit trail are kept. |
Memory | class | Long-term memory for an agent: facts recalled before each model call, and remember, forget and search_memory tools for the model. |
StorageOptions | interface | Settings for new Storage(...). |
MemoryOptions | interface | Settings for Memory.byState and Memory.shared. |
RetentionOptions | interface | Rules for deleting old data, applied by storage.prune(). |
Storage#
class Storage {
constructor(options?: string | StorageOptions);
}Where threads, long-term memory and the audit trail are kept.
Pass one to agents and workflows (their storage option) so that threads
survive restarts; agents built with the same storage share it. Without a
URL everything stays in process memory.
Parameters
options(string | StorageOptions, default{}): A URL, or the settings. Nothing: in-memory storage.
Raises
StorageError: The database cannot be opened, or the encryption keys are missing or do not match it.ValidationError: An option has a value the engine does not accept (e.g. an unknownonBusy).
Example
import { Storage } from "criogenai";
const storage = new Storage(); // or new Storage("sqlite:///data/app.db")
await storage.remember("users/u-1", "diet", "vegetarian");
const item = await storage.recall("users/u-1", "diet");
console.log(item?.value); // "vegetarian"
const hits = await storage.search("users", "vegetarian");
console.log(hits.length); // 1
await storage.close();Storage.url#
readonly url?: stringThe URL it was opened with; undefined for in-memory storage.
Storage.close#
close(): Promise<void>Closes the database's connections (SQLite releases the file). Later calls fail with StorageError.
Storage.remember#
remember(namespace: string, key: string, value: unknown, options?: { ttlSecs?: number; }): Promise<void>Saves a fact in long-term memory, replacing any value under the same key.
Facts saved here are found by keywords. (An agent with Memory.semantic
finds by meaning only the facts it saved itself.)
Parameters
namespace(string): A path such as"users/42": non-empty segments separated by/. The root""is refused.key(string): The fact's name in the namespace.value(unknown): Any JSON value, at most 1 MiB as JSON text.options({ ttlSecs?: number; }, default{}):ttlSecs: delete the fact this many seconds from now. Without it the fact is kept until deleted.
Raises
ValidationError: The namespace, key or value is not accepted (invalid_value).StorageError: The database failed.
Storage.recall#
recall(namespace: string, key: string): Promise<Record<string, unknown> | null>Reads one fact from long-term memory.
Parameters
namespace(string): The namespace it was saved in, e.g."users/42".key(string): Its key.
Returns: The item (namespace, key, value, created_at_ms, updated_at_ms, and expires_at_ms when it has a time to live), or null if it is missing or expired.
Storage.search#
search(namespace: string, text?: string, limit?: number): Promise<Record<string, unknown>[]>Searches long-term memory by keywords, in a namespace and the namespaces inside it.
An item matches when its key or the text in its value contains the query's words (common words such as "the" are ignored). Results are ranked by the share of words matched, then newest first.
Parameters
namespace(string): Where to search:"users"also covers"users/42".text(string, optional): The words to look for. Without them: every item, newest first.limit(number, default10): The most items to return.
Returns: Items as recall returns them, with a score from 0 to 1 when text was given.
Storage.forgetAll#
forgetAll(namespace: string): Promise<number>Deletes every fact in a namespace and the namespaces inside it, for example everything about one person.
Parameters
namespace(string): The namespace, e.g."users/42". The root""is refused.
Returns: How many items were deleted.
Storage.prune#
prune(): Promise<Record<string, number>>Applies the retention rules once and removes expired memory items.
Returns: Counts: threadsDeleted, threadsBusy (skipped because they were running), checkpointsDeleted, memoryItemsDeleted.
Storage.auditRecords#
auditRecords(query?: { threadId?: string; action?: string; afterSeq?: number; limit?: number; }): Promise<Record<string, unknown>[]>Reads the audit trail, oldest first. Needs audit: true.
Each record has seq (1, 2, 3, ...), at_ms, action (e.g.
"approval.decided"), when they apply actor, thread_id, run_id,
subject, outcome and details, and the chain's prev_hash and hash.
Parameters
query({ threadId?: string; action?: string; afterSeq?: number; limit?: number; }, default{}): Filters:threadId,action,afterSeq(only records after thisseq),limit(default 100).
Raises
ValidationError: The storage was opened withoutaudit: true.
Storage.verifyAudit#
verifyAudit(): Promise<{ records: number; ok: boolean; first_broken: number | null; }>Checks the audit trail's hash chain. Needs audit: true.
Returns: records checked, ok, and first_broken: the seq of the first record that does not match the chain, or null.
Raises
ValidationError: The storage was opened withoutaudit: true.
Memory#
class Memory {
constructor(spec: Record<string, unknown>);
}Long-term memory for an agent: facts recalled before each model call, and
remember, forget and search_memory tools for the model.
Facts live in the agent's storage, in a namespace such as users/42.
Create one with Memory.byState (one namespace per user) or
Memory.shared (one namespace for every run).
Parameters
spec(Record<string, unknown>): The settings, as the engine reads them.
Example
import { Agent, FakeModel, Memory, Storage } from "criogenai";
const storage = new Storage();
await storage.remember("users/u-1", "language", "Prefers answers in French.");
const agent = new Agent({
model: new FakeModel(["Bonjour !"]),
storage,
memory: Memory.byState("user_id", "users", { recall: 3 }),
});
const reply = await agent.run("Hello", { state: { user_id: "u-1" } });
console.log(reply.text); // "Bonjour !"Memory.byState#
static byState(key: string, prefix: string, options?: MemoryOptions): MemoryOne namespace per value of a run state key, e.g. one per user: prefix/<value>.
A run whose state lacks the key fails with invalid_state instead of
seeing someone else's facts.
Parameters
key(string): The state key whose value (a string or a number) picks the namespace, e.g."user_id".prefix(string): The namespace above it:"users"givesusers/<user_id>.options(MemoryOptions, default{}): Recall size, tools and time to live.
Memory.shared#
static shared(namespace: string, options?: MemoryOptions): MemoryOne namespace for every run: knowledge shared by all users.
Parameters
namespace(string): The namespace, e.g."team/facts".options(MemoryOptions, default{}): Recall size, tools and time to live.
Memory.semantic#
semantic(embedder: { readonly spec: Record<string, unknown>; }): MemorySearches by meaning with embedder: recall and search_memory find facts
that say the same thing in other words. If the embedder fails, search
falls back to keywords.
Facts the agent saves with its remember tool get a vector; facts saved
with storage.remember are found by keywords only.
Parameters
embedder({ readonly spec: Record<string, unknown>; }): AnEmbedder, e.g.Embedder.openai().
Returns: A new Memory with the same settings and the embedder.
StorageOptions#
interface StorageOptions {
url?: string;
encryptionKeys?: string;
audit?: boolean;
retention?: RetentionOptions;
onBusy?: "wait" | "reject" | "interrupt";
busyTimeoutSecs?: number;
}Settings for new Storage(...).
Fields
url(string, optional): Where data lives:"sqlite:///path/to/app.db"(a file),"postgres://user@host/db", or nothing to keep everything in process memory (lost when the process exits).encryptionKeys(string, optional): Encrypts content at rest:"k2:<64 hex digits>,k1:<64 hex digits>". The first key encrypts new rows; the others still decrypt older ones (key rotation). Used only with aurl. A database written with keys refuses to open without them. Keep the keys in an environment variable or a secret store, never in code.audit(boolean, defaultfalse): Keeps a tamper-evident audit trail of model calls, tool calls, runs, approvals, rewinds, deletions and memory writes: ids, names and outcomes, no content. Read it withauditRecords(), check it withverifyAudit().retention(RetentionOptions, optional): Rules for deleting old threads and checkpoints, applied byprune().onBusy("wait" | "reject" | "interrupt", default"wait"): What a second call on a thread that is already running does:"wait"for the running call to finish (at mostbusyTimeoutSecs, thenThreadBusyError),"reject"at once withThreadBusyError, or"interrupt"(cancel the running call, then go). Holds across processes on Postgres, within this process otherwise.busyTimeoutSecs(number, default60): How long"wait"and"interrupt"wait for the running call, in seconds.
MemoryOptions#
interface MemoryOptions {
recall?: number;
tools?: boolean;
ttlSecs?: number;
}Settings for Memory.byState and Memory.shared.
Fields
recall(number, default5): How many facts are added to the model's request before each model call (the best matches for the conversation, then the newest).0turns recall off; the tools still work.tools(boolean, defaulttrue): Whether the model gets theremember,forgetandsearch_memorytools.ttlSecs(number, optional): How long facts saved by theremembertool are kept, in seconds. Without it they are kept until deleted.
RetentionOptions#
interface RetentionOptions {
threadTtlSecs?: number;
keepCheckpoints?: number;
dropBranches?: boolean;
}Rules for deleting old data, applied by storage.prune(). Without any rule
everything is kept.
Fields
threadTtlSecs(number, optional): Deletes threads whose last save is this many seconds old or older, asthread.delete()would. Threads waiting for an approval count as idle.keepCheckpoints(number, optional): Keeps each thread's latest checkpoint and then - 1before it (at least one); older checkpoints and abandoned branches are deleted, and history then starts at the oldest kept one.dropBranches(boolean, optional): Deletes the branches thatrewindleft behind and keeps the current branch whole.keepCheckpointsdrops them too.