Skip to main content
Memory persists state across runs. Task outputs are per-run; memory is per-namespace and survives every workflow execution. The store keeps three kinds of state: namespaced facts (JSON values with optional TTL), ordered message threads, and append-only notes (durable knowledge with supersession and a status gate). 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.
createMemoryStore(db) preserves the local SQLite implementation. Pass that store as contractStore when constructing the Hindsight adapter:
The contract store remains authoritative for atomic exact records and global message and note ids. Hindsight projection is best-effort: a remote failure is logged and queued for an in-process retry, but it does not reject a contract mutation that already committed. The queue is not durable across process restarts. The HindsightMemoryStore class is exported for callers that need its recallMemory, getPrimers, or retainMemory engine-facing methods.
The note types (MemoryNote, SaveNoteInput, NoteReadFilter, and MemoryProvenance) are exported from the smithers-orchestrator/memory subpath. WorkingMemoryConfig<T> is generic over a Zod schema and is documented inline below. Task can be imported directly or taken from createSmithers; both expose the same memory prop. See Memory for the declarative <Task memory={...}> metadata.

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.
kind
"workflow" | "agent" | "user" | "global"
required
Lifetime scope. workflow is per workflow definition, agent per agent identity, user per end user, global shared across everything.
id
string
required
Identifier within the kind.
A fact is a namespaced JSON value, last-write-wins, with an optional TTL.
MemoryFact
object
A thread groups ordered messages.
MemoryThread
object
MemoryMessage
object
A note is append-only knowledge. Facts are mutable KV (upsert semantics); notes are immutable rows: body, labels, and provenance never change 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.
MemoryNote
object
MemoryProvenance is the run coordinate a memory write was made from. It is passed explicitly by the caller on setFact and saveNote, never inferred from ambient context, which does not survive agent/tool boundaries.
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 (pass one to make retries idempotent). NoteReadFilter shapes listNotes/searchNotes reads. The default read contract (no filter) is a stability contract: it 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:

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.
db
BunSQLiteDatabase
required
A Drizzle bun-sqlite database handle. The memory tables are created on the same database your workflow uses.

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.
Facts
methods
Notes
methods
Note writes and search require the bun:sqlite backend: transactions provide note+edge atomicity and FTS5 provides search. Migration 0023 creates the note tables on Postgres/PGlite, but the runtime still rejects saveNote and enableNoteSearch there with DB_WRITE_FAILED, and searchNotes with DB_QUERY_FAILED.
Threads & messages
methods
Thread and message reads/writes use the portable Drizzle tables. The transactional deleteThread operation is the exception: it currently requires bun:sqlite and raises DB_WRITE_FAILED on Postgres/PGlite.

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.
MemoryServiceApi
object

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.
config
MemoryLayerConfig
required

Processors

A MemoryProcessor is a named maintenance pass over a MemoryStore. Each exposes a Promise process(store) and an Effect processEffect(store).
Summarizer
(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.
TokenLimiter
(maxTokens) => MemoryProcessor
Trims oldest messages per thread until each thread fits a rough token budget (approximated as maxTokens * 4 characters).
TtlGarbageCollector
() => MemoryProcessor
Deletes expired facts across all namespaces by calling deleteExpiredFacts.
MemoryProcessorConfig selects which named processors to run.
MemoryProcessorConfig
object

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.
memory
TaskMemoryConfig
Object-form recall, remember, namespace, and threadId do not inject facts, retain output, or append message history. Use the bank-based fields for runtime memory, or use the store APIs directly for exact local records. SemanticRecallConfig and MessageHistoryConfig describe the two recall strategies a memory-aware runtime can apply.
SemanticRecallConfig
object
MessageHistoryConfig
object
Working memory is a typed, single-document fact validated against a Zod schema. Its config is generic over the schema type:

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.
namespaceToString
(ns: MemoryNamespace) => string
Serializes a namespace, e.g. { kind: "user", id: "u1" } becomes "user:u1".
parseNamespace
(str: string) => MemoryNamespace
Parses a serialized namespace back. Strings without a known kind prefix parse as { kind: "global", id: str }.
Cross-run facts are also viewable from the CLI:
Source store/MemoryStore.ts · MemoryServiceApi.ts · MemoryNote.ts · createMemoryLayer.js · processors.js · Tests store.test.js · notes.test.js · service.test.js · processors.test.js · See also Memory, Types reference