Skip to main content
Memory persists state across runs. Task outputs are per-run; memory is per-namespace and survives every workflow execution.
API reference: Memory lists every memory export and type, its options, and links to source and tests.

Three layers

import { createMemoryStore } from "smithers-orchestrator/memory";
import { Database } from "bun:sqlite";
import { drizzle } from "drizzle-orm/bun-sqlite";

const sqlite = new Database("smithers.db");
const db = drizzle(sqlite);
const store = createMemoryStore(db);
const ns = { kind: "workflow" as const, id: "code-review" };
LayerAPIUse for
Factsstore.setFact(ns, key, value, ttlMs?) / store.getFact(ns, key) / store.listFacts(ns)Namespaced JSON facts. Optional TTL. Last-write-wins.
Message historystore.createThread(ns, title?), store.listThreads(ns), store.saveMessage(msg), store.listMessages(threadId, limit), store.deleteMessages(threadId)Ordered chat threads per agent or user; list/delete to compact history.
Maintenancestore.deleteExpiredFacts() plus processorsTTL cleanup and history compaction.
Every method has an Effect-returning twin (store.listThreadsEffect, store.deleteMessagesEffect, etc.) for use inside an Effect pipeline.

Namespaces

type MemoryNamespace = { kind: "workflow" | "agent" | "user" | "global"; id: string };
Pick the kind to match the lifetime: workflow is scoped to a workflow definition; agent to an agent identity; user to an end user; global is shared across everything.

Task Memory Metadata

<Task
  id="review"
  output={outputs.review}
  agent={reviewer}
  memory={{
    recall:   { namespace: ns, topK: 3 },               // inject relevant past facts into prompt
    remember: { namespace: ns, key: "last-review" },    // persist this output as a fact
    threadId: `${ctx.input.repo}:reviews`,              // append messages to this thread
  }}
>
  Review the latest PR.
</Task>
memory is preserved on the task descriptor as metadata for runtimes and integrations that layer memory behavior onto task execution. The direct store APIs above are the current public memory read/write surface.

Imperative get/set/delete inside a workflow

The memory={{ recall, remember }} block above is declarative metadata; it is preserved on the task descriptor for runtimes that layer memory onto task execution, not an imperative call. To actually get, set, or delete a fact while a run is executing, build a store with createMemoryStore(db) and call it from inside a compute <Task> (a function task with no agent). The compute callback receives deps only; there is no injected store, so you create one:
import { createMemoryStore } from "smithers-orchestrator/memory";
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" };

<Task id="remember-model" output={outputs.done}>
  {async () => {
    await store.setFact(ns, "model", "gpt-4");           // set
    const fact = await store.getFact(ns, "model");        // get (MemoryFact | undefined)
    const all  = await store.listFacts(ns);               // list
    await store.deleteFact(ns, "stale-key");              // delete
    return { done: true };
  }}
</Task>
Create the store once at module scope (outside the workflow) and reuse it across tasks rather than re-opening the SQLite handle in every task body. Under Effect, the same operations are available as store.setFactEffect(ns, key, value, ttlMs?), store.getFactEffect(ns, key), etc., or via MemoryService (MemoryServiceApi), which also exposes the underlying .store.

Processors

Maintenance jobs you run periodically:
import { TtlGarbageCollector, TokenLimiter, Summarizer } from "smithers-orchestrator/memory";

const gc         = TtlGarbageCollector();   // expire facts past their TTL
const limiter    = TokenLimiter(4000);      // keep history under token budget
const summarizer = Summarizer(myAgent);     // compress old messages with an LLM

await gc.process(store);
await limiter.process(store);
await summarizer.process(store);

Inspect from the CLI

bunx smithers-orchestrator memory list workflow:code-review -w workflow.tsx
The CLI currently exposes fact listing. Use the store API for writes, deletes, threads, messages, and TTL cleanup.

Notes

  • Memory and task outputs are distinct stores. Don’t use memory for run-scoped state; it’s not transactional with the workflow’s frame commits.
  • Working-memory writes are unordered. Use message history when sequence matters.