> ## Documentation Index
> Fetch the complete documentation index at: https://smithers.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory API

> Cross-run facts, message threads, namespaces, and the Effect store layer.

Memory persists state **across runs**: task outputs are per-run, memory is
per-namespace. The store keeps three kinds of state, detailed below:
namespaced **facts**, ordered **message threads**, and append-only **notes**.

All memory values and non-generic types are re-exported from the canonical
`smithers-orchestrator` facade; the `smithers-orchestrator/memory` subpath
exports the same surface.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import {
  createMemoryStore,
  createHindsightMemoryStore,
  HindsightMemoryStore,
  createMemoryLayer,
  MemoryService,
  Summarizer,
  TokenLimiter,
  TtlGarbageCollector,
  namespaceToString,
  parseNamespace,
} from "smithers-orchestrator";
import type {
  MemoryNamespace,
  MemoryNamespaceKind,
  MemoryFact,
  MemoryThread,
  MemoryMessage,
  MemoryStore,
  MemoryServiceApi,
  MemoryProcessor,
  MemoryProcessorConfig,
  MemoryLayerConfig,
  HindsightMemoryStoreOptions,
  TaskMemoryConfig,
  SemanticRecallConfig,
  MessageHistoryConfig,
} from "smithers-orchestrator";
```

`createMemoryStore(db)` preserves the local SQLite implementation; pass it as
`contractStore` when constructing the Hindsight adapter:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const contractStore = createMemoryStore(db);
const memory = createHindsightMemoryStore({
  baseUrl: process.env.HINDSIGHT_URL!,
  contractStore,
  apiKey: process.env.HINDSIGHT_API_KEY,
});
```

`HINDSIGHT_URL` is the base URL of a reachable
[Hindsight](https://hindsight.vectorize.io) deployment: `http://127.0.0.1:8888`
for the default self-hosted Docker image, or a Hindsight Cloud workspace URL.
Obtaining one (cloud signup or self-host deploy) is a human step, covered in
the Product API at
[Set Up Semantic Memory](/guide/setup/semantic-memory); `HINDSIGHT_API_KEY` is
the bearer token for endpoints that require one.

The contract store stays authoritative for atomic exact records and global
message/note ids; Hindsight projection is best-effort, logging and queueing a
remote failure for in-process retry (not durable across restarts) without
rejecting an already-committed contract mutation. `HindsightMemoryStore` is
exported for its `recallMemory`, `getPrimers`, and `retainMemory`
engine-facing methods.

<Note>
  Note types (`MemoryNote`, `SaveNoteInput`, `NoteReadFilter`, `MemoryProvenance`)
  are exported from `smithers-orchestrator/memory`. `WorkingMemoryConfig<T>` is
  generic over a Zod schema (documented below). `Task`, imported directly or
  from `createSmithers`, exposes the same `memory` prop; see
  [Memory](/concepts/memory) for the declarative `<Task memory={...}>` metadata.
</Note>

## Concepts

A `MemoryNamespace` scopes everything you store: `kind` matches the data's
lifetime, `id` identifies the specific workflow, agent, or user.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
type MemoryNamespace = { kind: MemoryNamespaceKind; id: string };
type MemoryNamespaceKind = "workflow" | "agent" | "user" | "global";
```

<ParamField path="kind" type="&#x22;workflow&#x22; | &#x22;agent&#x22; | &#x22;user&#x22; | &#x22;global&#x22;" required>
  Lifetime scope: `workflow` per workflow definition, `agent` per agent
  identity, `user` per end user, `global` shared across everything.
</ParamField>

<ParamField path="id" type="string" required>
  Identifier within the kind.
</ParamField>

A **fact** is a namespaced JSON value, last-write-wins, with an optional TTL.

<ResponseField name="MemoryFact" type="object">
  <Expandable title="MemoryFact">
    <ResponseField name="namespace" type="string" required>
      The namespace, serialized via `namespaceToString`.
    </ResponseField>

    <ResponseField name="key" type="string" required>
      Fact key, unique within the namespace.
    </ResponseField>

    <ResponseField name="valueJson" type="string" required>
      The value, JSON-stringified.
    </ResponseField>

    <ResponseField name="schemaSig" type="string | null">
      Optional schema signature for the stored value.
    </ResponseField>

    <ResponseField name="createdAtMs" type="number" required>
      Creation time, epoch milliseconds.
    </ResponseField>

    <ResponseField name="updatedAtMs" type="number" required>
      Last-write time, epoch milliseconds.
    </ResponseField>

    <ResponseField name="ttlMs" type="number | null">
      Time-to-live in milliseconds. Expired facts are removed by
      `deleteExpiredFacts` and `TtlGarbageCollector`.
    </ResponseField>
  </Expandable>
</ResponseField>

A **thread** groups ordered **messages**.

<ResponseField name="MemoryThread" type="object">
  <Expandable title="MemoryThread">
    <ResponseField name="threadId" type="string" required>
      Thread identifier.
    </ResponseField>

    <ResponseField name="namespace" type="string" required>
      Owning namespace, serialized.
    </ResponseField>

    <ResponseField name="title" type="string | null">
      Optional human-readable title.
    </ResponseField>

    <ResponseField name="metadataJson" type="string | null">
      Optional JSON metadata.
    </ResponseField>

    <ResponseField name="createdAtMs" type="number" required />

    <ResponseField name="updatedAtMs" type="number" required />
  </Expandable>
</ResponseField>

<ResponseField name="MemoryMessage" type="object">
  <Expandable title="MemoryMessage">
    <ResponseField name="id" type="string" required>
      Message identifier.
    </ResponseField>

    <ResponseField name="threadId" type="string" required>
      Owning thread.
    </ResponseField>

    <ResponseField name="role" type="string" required>
      Message role (e.g. `"user"`, `"assistant"`, `"system"`).
    </ResponseField>

    <ResponseField name="contentJson" type="string" required>
      Message content, JSON-stringified.
    </ResponseField>

    <ResponseField name="runId" type="string | null">
      Run that produced the message, if any (`RUN_ID`).
    </ResponseField>

    <ResponseField name="nodeId" type="string | null">
      Node that produced the message, if any (`NODE_ID`).
    </ResponseField>

    <ResponseField name="createdAtMs" type="number" required>
      Creation time, epoch milliseconds. Optional on `saveMessage` input
      (defaults to now).
    </ResponseField>
  </Expandable>
</ResponseField>

A **note** is append-only knowledge: facts are mutable KV, notes are immutable
rows, body/labels/provenance fixed after insert. `status` is the one
deliberate exception: `setNoteStatus` lets a human or workflow gate write an
answer about an existing note without churning its id. Notes carry no TTL:
knowledge dies by supersession or rejection, not by clock.

<ResponseField name="MemoryNote" type="object">
  <Expandable title="MemoryNote">
    <ResponseField name="id" type="string" required>
      Note identifier (see `saveNote` below for idempotency).
    </ResponseField>

    <ResponseField name="namespace" type="string" required>
      Owning namespace, serialized via `namespaceToString`.
    </ResponseField>

    <ResponseField name="body" type="string" required>
      The knowledge itself. Immutable after insert.
    </ResponseField>

    <ResponseField name="kind" type="string | null">
      Optional, policy-free label (e.g. `"lesson"`, `"runbook-rule"`). The
      engine never interprets it.
    </ResponseField>

    <ResponseField name="tagsJson" type="string | null">
      JSON-encoded string array; null when the note has no tags.
    </ResponseField>

    <ResponseField name="author" type="string | null">
      Optional, policy-free author label.
    </ResponseField>

    <ResponseField name="status" type="string" required>
      Free-form, defaulting to `"accepted"` (conventionally
      `pending | accepted | rejected`); the one mutable field.
    </ResponseField>

    <ResponseField name="statusChangedAtMs" type="number | null">
      When `setNoteStatus` last flipped the status; null until then.
    </ResponseField>

    <ResponseField name="createdAtMs" type="number" required>
      Creation time, epoch milliseconds.
    </ResponseField>

    <ResponseField name="runId / nodeId / iteration" type="string | string | number | null">
      Provenance: the writing run's coordinate (see `MemoryProvenance`).
    </ResponseField>
  </Expandable>
</ResponseField>

`MemoryProvenance` is the run coordinate a memory write was made from, passed
**explicitly** by the caller on `setFact` and `saveNote`. It's never inferred
from ambient context, which doesn't survive agent/tool boundaries.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
type MemoryProvenance = {
  runId?: string | null;
  nodeId?: string | null;
  iteration?: number | null;
};
```

`SaveNoteInput` is the `saveNote` argument: the namespace as the structured
object, tags as an array, plus optional `kind`, `author`, `status`,
`provenance`, `supersedes` (note ids this note replaces; junction rows are
written atomically with the note), and `id` (idempotency key, see `saveNote`).

`NoteReadFilter` shapes `listNotes`/`searchNotes` reads. The **default read
contract** (no filter): returns notes that are (a) not superseded by an
**accepted** note and (b) `status = "accepted"`; a pending or rejected
superseder hides nothing. Filters widen or narrow:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
type NoteReadFilter = {
  status?: string | string[] | "any";
  includeSuperseded?: boolean;
  kind?: string;
  namespace?: MemoryNamespace; // scope searchNotes to one namespace of the kind
};
```

## createMemoryStore

Build a `MemoryStore` over a Drizzle SQLite handle. Synchronous; create one at
module scope and reuse it across tasks instead of reopening the database per
task.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
function createMemoryStore(db: BunSQLiteDatabase<any>): MemoryStore;
```

<ParamField path="db" type="BunSQLiteDatabase" required>
  A Drizzle `bun-sqlite` database handle; the memory tables live on the same
  database your workflow uses.
</ParamField>

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { createMemoryStore } from "smithers-orchestrator";
import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";

const store = createMemoryStore(drizzle(new Database("smithers.db")));
const ns = { kind: "workflow" as const, id: "code-review" };

await store.setFact(ns, "model", "gpt-4");
const fact = await store.getFact(ns, "model"); // MemoryFact | undefined
const all = await store.listFacts(ns);
```

## MemoryStore

The Promise-based read/write surface. Every method has an Effect-returning twin
(`getFactEffect`, `setFactEffect`, `listThreadsEffect`, `deleteMessagesEffect`,
etc.) with the same arguments, for use inside an Effect pipeline.

<ResponseField name="Facts" type="methods">
  <Expandable title="fact methods">
    <ResponseField name="getFact" type="(ns, key) => Promise<MemoryFact | undefined>" />

    <ResponseField name="setFact" type="(ns, key, value, ttlMs?) => Promise<void>">
      Last-write-wins. `value` is any JSON-serializable value; `ttlMs` sets an
      optional expiry.
    </ResponseField>

    <ResponseField name="deleteFact" type="(ns, key) => Promise<void>" />

    <ResponseField name="listFacts" type="(ns) => Promise<MemoryFact[]>" />

    <ResponseField name="deleteExpiredFacts" type="() => Promise<number>">
      Removes facts whose TTL elapsed; returns the count deleted.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="Notes" type="methods">
  <Expandable title="note methods">
    <ResponseField name="saveNote" type="(input: SaveNoteInput) => Promise<MemoryNote>">
      Appends a note and its supersession edges atomically. Idempotent on
      `id`: re-saving the same id is a no-op (never an upsert, so history
      can't be destroyed); the returned note is the row the database holds,
      not the ignored input.
    </ResponseField>

    <ResponseField name="getNote" type="(id) => Promise<MemoryNote | undefined>" />

    <ResponseField name="listNotes" type="(ns, filter?) => Promise<MemoryNote[]>">
      Notes in one namespace, oldest first, under the default read contract
      unless `filter` widens.
    </ResponseField>

    <ResponseField name="setNoteStatus" type="(id, status) => Promise<void>">
      The one mutable write: flips `status` and stamps `statusChangedAtMs`.
      Fails loud when the note does not exist.
    </ResponseField>

    <ResponseField name="enableNoteSearch" type="(kind) => Promise<void>">
      Opts a namespace **kind** into FTS note search, creating FTS5 artifacts
      lazily on first call and backfilling existing notes of that kind; until
      then note writes pay nothing extra. Idempotent.
    </ResponseField>

    <ResponseField name="searchNotes" type="(kind, query, limit?, filter?) => Promise<MemoryNote[]>">
      Full-text search over note bodies, rank-ordered, honoring the same read
      contract as `listNotes`. Fails loud (not silently empty) when the kind
      was never enabled. The index spans **every namespace of the kind**;
      pass `filter.namespace` to keep results namespace-local on a shared
      database. The read contract applies only to the top `limit * 5` FTS
      matches, so a heavily superseded corpus may return fewer than `limit`
      despite more live matches further down.
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Note writes and search require bun:sqlite: transactions give note+edge
  atomicity, FTS5 gives search. Migration 0023 creates note tables on
  Postgres/PGlite, but the runtime still rejects `saveNote`/`enableNoteSearch`
  there with `DB_WRITE_FAILED`, and `searchNotes` with `DB_QUERY_FAILED`.
</Note>

<ResponseField name="Threads & messages" type="methods">
  Thread and message reads/writes use the portable Drizzle tables, except the
  transactional `deleteThread`, which requires bun:sqlite and raises
  `DB_WRITE_FAILED` on Postgres/PGlite.

  <Expandable title="thread and message methods">
    <ResponseField name="createThread" type="(ns, title?) => Promise<MemoryThread>" />

    <ResponseField name="getThread" type="(threadId) => Promise<MemoryThread | undefined>" />

    <ResponseField name="listThreads" type="() => Promise<MemoryThread[]>" />

    <ResponseField name="deleteThread" type="(threadId) => Promise<void>" />

    <ResponseField name="saveMessage" type="(msg) => Promise<void>">
      `msg` is a `MemoryMessage` with an optional `createdAtMs` (defaults to
      now).
    </ResponseField>

    <ResponseField name="listMessages" type="(threadId, limit?) => Promise<MemoryMessage[]>" />

    <ResponseField name="countMessages" type="(threadId) => Promise<number>" />

    <ResponseField name="deleteMessages" type="(threadId, messageIds) => Promise<number>">
      Deletes the named messages, returning the count deleted; used by
      processors to compact history.
    </ResponseField>
  </Expandable>
</ResponseField>

## MemoryService

An Effect `Context.Tag` whose service value is a `MemoryServiceApi`: the same
operations as the store, but every method returns
`Effect.Effect<T, SmithersError>` instead of a `Promise`. The underlying store
is reachable via `.store`.

<ResponseField name="MemoryServiceApi" type="object">
  <Expandable title="members">
    <ResponseField name="getFact / setFact / deleteFact / listFacts" type="Effect">
      Fact operations, Effect-returning. `setFact(ns, key, value, ttlMs?)`.
    </ResponseField>

    <ResponseField name="createThread / getThread / deleteThread" type="Effect">
      Thread lifecycle, Effect-returning.
    </ResponseField>

    <ResponseField name="saveMessage / listMessages / countMessages" type="Effect">
      Message operations, Effect-returning.
    </ResponseField>

    <ResponseField name="saveNote / getNote / listNotes / setNoteStatus / enableNoteSearch / searchNotes" type="Effect">
      Note operations, Effect-returning, with the same semantics as the store
      methods above.
    </ResponseField>

    <ResponseField name="deleteExpiredFacts" type="() => Effect<number, SmithersError>" />

    <ResponseField name="store" type="MemoryStore">
      The underlying Promise-and-Effect store, for methods the service doesn't
      surface directly (e.g. `listThreads`, `deleteMessages`).
    </ResponseField>
  </Expandable>
</ResponseField>

## createMemoryLayer

Build the Effect `Layer` that provides `MemoryService`. Pass it a
`MemoryLayerConfig` carrying the Drizzle database; provide the resulting layer
to any Effect that depends on `MemoryService`.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
function createMemoryLayer(
  config: MemoryLayerConfig,
): Layer.Layer<MemoryService, never, never>;
```

<ParamField path="config" type="MemoryLayerConfig" required>
  <Expandable title="MemoryLayerConfig">
    <ParamField path="db" type="BunSQLiteDatabase" required>
      The Drizzle `bun-sqlite` handle the memory tables live on.
    </ParamField>
  </Expandable>
</ParamField>

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { Effect } from "effect";
import { createMemoryLayer, MemoryService } from "smithers-orchestrator";

const program = Effect.gen(function* () {
  const memory = yield* MemoryService;
  yield* memory.setFact({ kind: "user", id: "u1" }, "tier", "pro");
});

Effect.runPromise(program.pipe(Effect.provide(createMemoryLayer({ db }))));
```

## Processors

A `MemoryProcessor` is a named maintenance pass over a `MemoryStore`. Each
exposes a Promise `process(store)` and an Effect `processEffect(store)`.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
type MemoryProcessor = {
  name: string;
  process: (store: MemoryStore) => Promise<void>;
  processEffect: (store: MemoryStore) => Effect.Effect<void, SmithersError>;
};
```

<ResponseField name="Summarizer" type="(agent) => MemoryProcessor">
  Compresses older messages in each thread into one `system` summary message,
  keeping the two most recent. `agent` is any
  `{ run: (prompt: string) => Promise<unknown> }` whose output text becomes
  the summary.
</ResponseField>

<ResponseField name="TokenLimiter" type="(maxTokens) => MemoryProcessor">
  Trims oldest messages per thread until each thread fits a rough token budget
  (approximated as `maxTokens * 4` characters).
</ResponseField>

<ResponseField name="TtlGarbageCollector" type="() => MemoryProcessor">
  Deletes expired facts across all namespaces by calling
  `deleteExpiredFacts`.
</ResponseField>

`MemoryProcessorConfig` selects which named processors to run.

<ResponseField name="MemoryProcessorConfig" type="object">
  <Expandable title="MemoryProcessorConfig">
    <ResponseField name="processors" type="string[]">
      E.g. `["Summarizer", "TtlGarbageCollector"]`.
    </ResponseField>
  </Expandable>
</ResponseField>

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { TtlGarbageCollector, Summarizer } from "smithers-orchestrator";

await TtlGarbageCollector().process(store);
await Summarizer(myAgent).process(store);
```

## Task-level config

`TaskMemoryConfig` is the shape of the `memory` prop on `Task`. A `bank` or
`banks` selection activates engine recall, primers, retention, and tools.

<ParamField path="memory" type="TaskMemoryConfig">
  <Expandable title="TaskMemoryConfig">
    <ParamField path="bank" type="string">
      One memory bank. Mutually exclusive with `banks`.
    </ParamField>

    <ParamField path="banks" type="string[]">
      Memory banks recalled in parallel. Mutually exclusive with `bank`.
    </ParamField>

    <ParamField path="tags" type="string[]">
      Stable branch, stream, source, and scope dimensions for recall and retention.
    </ParamField>

    <ParamField path="recall" type="&#x22;auto&#x22; | string | false | { namespace?; query?; topK? }">
      `"auto"` derives the query from the task prompt, a string supplies the
      query, `false` disables prompt recall. The object form is legacy
      compatibility metadata, preserved but inert.
    </ParamField>

    <ParamField path="budget" type="&#x22;low&#x22; | &#x22;mid&#x22; | &#x22;high&#x22;">
      Hindsight candidate-search budget. Defaults to `"mid"`.
    </ParamField>

    <ParamField path="maxTokens" type="number">
      Hard cap for the injected memory block and serialized recall tool
      result. Defaults to `2048`.
    </ParamField>

    <ParamField path="primers" type="string[]">
      Mental-model ids fetched across the selected banks and injected verbatim.
    </ParamField>

    <ParamField path="retain" type="&#x22;on-complete&#x22; | &#x22;off&#x22;">
      Retain successful task output asynchronously, or disable automatic
      retention. Defaults to `"off"`.
    </ParamField>

    <ParamField path="tools" type="boolean">
      Expose `remember` and `recall` tools to the task agent. Defaults to `false`.
    </ParamField>

    <ParamField path="namespace" type="string | MemoryNamespace">
      Legacy compatibility metadata. Preserved but inert.
    </ParamField>

    <ParamField path="remember" type="{ namespace?; key? }">
      Legacy compatibility metadata. Preserved but inert.
    </ParamField>

    <ParamField path="threadId" type="string">
      Legacy compatibility metadata. Preserved but inert.
    </ParamField>
  </Expandable>
</ParamField>

Object-form `recall`, `remember`, `namespace`, and `threadId` inject no facts,
retain no output, and append no message history. Use the bank-based fields for
runtime memory, or the store APIs directly for exact local records.

`SemanticRecallConfig` and `MessageHistoryConfig` describe the two recall
strategies a memory-aware runtime can apply.

<ResponseField name="SemanticRecallConfig" type="object">
  <Expandable title="SemanticRecallConfig">
    <ResponseField name="topK" type="number">
      Maximum number of facts to recall.
    </ResponseField>

    <ResponseField name="namespace" type="MemoryNamespace">
      Namespace to recall from.
    </ResponseField>

    <ResponseField name="similarityThreshold" type="number">
      Minimum similarity score for a match.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="MessageHistoryConfig" type="object">
  <Expandable title="MessageHistoryConfig">
    <ResponseField name="lastMessages" type="number">
      How many recent messages to include.
    </ResponseField>

    <ResponseField name="threadId" type="string">
      Thread to read history from.
    </ResponseField>
  </Expandable>
</ResponseField>

Working memory is a typed, single-document fact validated against a Zod schema.
Its config is generic over the schema type:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
type WorkingMemoryConfig<
  T extends import("zod").ZodObject<import("zod").ZodRawShape> = import("zod").ZodObject<import("zod").ZodRawShape>,
> = {
  schema?: T;
  namespace: MemoryNamespace;
  ttlMs?: number;
};
```

## Helpers

Namespaces are stored as strings. These two helpers round-trip a
`MemoryNamespace` to and from its canonical `kind:id` form, percent-encoding
`:` and `%` in the id.

<ResponseField name="namespaceToString" type="(ns: MemoryNamespace) => string">
  Serializes a namespace, e.g. `{ kind: "user", id: "u1" }` becomes
  `"user:u1"`.
</ResponseField>

<ResponseField name="parseNamespace" type="(str: string) => MemoryNamespace">
  Parses a serialized namespace back. Strings without a known kind prefix parse
  as `{ kind: "global", id: str }`.
</ResponseField>

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { namespaceToString, parseNamespace } from "smithers-orchestrator";

namespaceToString({ kind: "user", id: "u1" }); // "user:u1"
parseNamespace("workflow:code-review"); // { kind: "workflow", id: "code-review" }
```

Cross-run facts are also viewable from the CLI:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
bunx smithers-orchestrator memory
```

**Source** [`store/MemoryStore.ts`](https://github.com/smithersai/smithers/blob/main/packages/memory/src/store/MemoryStore.ts) · [`MemoryServiceApi.ts`](https://github.com/smithersai/smithers/blob/main/packages/memory/src/MemoryServiceApi.ts) · [`MemoryNote.ts`](https://github.com/smithersai/smithers/blob/main/packages/memory/src/MemoryNote.ts) · [`createMemoryLayer.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/src/createMemoryLayer.js) · [`processors.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/src/processors.js) · **Tests** [`store.test.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/tests/store.test.js) · [`notes.test.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/tests/notes.test.js) · [`service.test.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/tests/service.test.js) · [`processors.test.js`](https://github.com/smithersai/smithers/blob/main/packages/memory/tests/processors.test.js) · **See also** [Memory](/concepts/memory), [Types reference](/reference/types)
