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#

NameKindSummary
StorageclassWhere threads, long-term memory and the audit trail are kept.
MemoryclassLong-term memory for an agent: facts recalled before each model call, and remember, forget and search_memory tools for the model.
StorageOptionsinterfaceSettings for new Storage(...).
MemoryOptionsinterfaceSettings for Memory.byState and Memory.shared.
RetentionOptionsinterfaceRules 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 unknown onBusy).

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?: string

The 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, default 10): 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 this seq), limit (default 100).

Raises

  • ValidationError: The storage was opened without audit: 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 without audit: 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): Memory

One 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" gives users/<user_id>.
  • options (MemoryOptions, default {}): Recall size, tools and time to live.

Memory.shared#

static shared(namespace: string, options?: MemoryOptions): Memory

One 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>; }): Memory

Searches 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>; }): An Embedder, 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 a url. 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, default false): 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 with auditRecords(), check it with verifyAudit().
  • retention (RetentionOptions, optional): Rules for deleting old threads and checkpoints, applied by prune().
  • 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 most busyTimeoutSecs, then ThreadBusyError), "reject" at once with ThreadBusyError, or "interrupt" (cancel the running call, then go). Holds across processes on Postgres, within this process otherwise.
  • busyTimeoutSecs (number, default 60): 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, default 5): How many facts are added to the model's request before each model call (the best matches for the conversation, then the newest). 0 turns recall off; the tools still work.
  • tools (boolean, default true): Whether the model gets the remember, forget and search_memory tools.
  • ttlSecs (number, optional): How long facts saved by the remember tool 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, as thread.delete() would. Threads waiting for an approval count as idle.
  • keepCheckpoints (number, optional): Keeps each thread's latest checkpoint and the n - 1 before 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 that rewind left behind and keeps the current branch whole. keepCheckpoints drops them too.