> ## Documentation Index
> Fetch the complete documentation index at: https://smithers-feat-claude-workflow-mirror.mintlify.site/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 and survives every workflow execution. The store keeps two kinds
of state: namespaced **facts** (JSON values with optional TTL) and ordered
**message threads**.

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

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

<Note>
  `WorkingMemoryConfig<T>` is generic over a Zod schema and is documented inline
  below rather than imported. The `memory` prop on `Task` is **returned by the
  factory**, not imported; see [Memory](/concepts/memory) for the declarative
  `<Task memory={...}>` metadata.
</Note>

## Concepts

A `MemoryNamespace` scopes everything you store. Pick the `kind` to match the
lifetime of the data, and the `id` to identify the specific workflow, agent, or
user.

```ts theme={null}
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` is 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. Use `RUN_ID`.
    </ResponseField>

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

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

## createMemoryStore

Build a `MemoryStore` over a Drizzle SQLite handle. Synchronous. Create one at
module scope and reuse it across tasks rather than re-opening the database in
every task body.

```ts theme={null}
function createMemoryStore(db: BunSQLiteDatabase<any>): MemoryStore;
```

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

```ts theme={null}
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`,
and so on) 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 has elapsed. Returns the count deleted.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="Threads & messages" type="methods">
  <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. Returns the count deleted. Used by processors
      to compact history.
    </ResponseField>
  </Expandable>
</ResponseField>

## MemoryService

An Effect `Context.Tag` whose service value is a `MemoryServiceApi`. It exposes
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="deleteExpiredFacts" type="() => Effect<number, SmithersError>" />

    <ResponseField name="store" type="MemoryStore">
      The underlying Promise-and-Effect store, for the few methods the service
      does not 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={null}
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={null}
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={null}
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 a single `system` summary
  message, keeping the two most recent. `agent` is any
  `{ run: (prompt: string) => Promise<unknown> }`; its 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[]">
      Names of the processors to run, e.g. `["Summarizer", "TtlGarbageCollector"]`.
    </ResponseField>
  </Expandable>
</ResponseField>

```ts theme={null}
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`. It is preserved
as task metadata for runtimes and integrations that layer memory behavior onto
task execution; the store APIs above are the current public read/write surface.

<ParamField path="memory" type="TaskMemoryConfig">
  <Expandable title="TaskMemoryConfig">
    <ParamField path="namespace" type="string | MemoryNamespace">
      Default namespace for this task's memory.
    </ParamField>

    <ParamField path="recall" type="{ namespace?; query?; topK? }">
      Inject relevant past facts into the prompt. `namespace` overrides the
      default, `query` is the recall query, `topK` caps results.
    </ParamField>

    <ParamField path="remember" type="{ namespace?; key? }">
      Persist this task's output as a fact under `key` in `namespace`.
    </ParamField>

    <ParamField path="threadId" type="string">
      Thread this task's messages are appended to.
    </ParamField>
  </Expandable>
</ParamField>

`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={null}
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={null}
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={null}
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) · [`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) · [`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)
