Maniac Docs
Memory

Memory adapters

InMemoryMemory, SqliteMemory, HonchoMemory, and VectorMemory — when to use each Memory implementation.

Memory adapters

Every adapter implements the Memory interface from @maniac-ai/agents/schemas:

interface Memory {
  save(record: MemoryRecord, scope: MemoryScope): Promise<string>;
  load(id: string, scope: MemoryScope): Promise<MemoryRecord | null>;
  search(query: MemoryQuery, scope: MemoryScope): Promise<MemoryRecord[]>;
  delete(id: string, scope: MemoryScope): Promise<boolean>;
}

Records carry a namespace, JSON content, and metadata. Namespaces follow predictable paths such as agent:<agentId>/threads/<threadId>/messages.

InMemoryMemory

The default for tests and quickstarts. Stores records in an in-process Map with monotonic ordering and implements MemorySequencer for turn indices.

import { InMemoryMemory, Maniac } from "@maniac-ai/agents";

const app = new Maniac({
  memory: new InMemoryMemory(),
  agents: [myAgent]
});

search applies metadata filters and case-insensitive substring matching over JSON.stringify(content). There is no vector index — use VectorMemory when you need semantic recall.

SqliteMemory

Durable, single-process storage backed by Node's built-in node:sqlite. Zero additional npm dependencies and no native build step.

When to use it

  • You want durable conversation, observation, and checkpoint storage on one host
  • You can tolerate SQLite's single-writer concurrency model (one process writes; readers proceed concurrently under WAL)

Reach for Postgres when you need multi-host write concurrency or rich JSON containment queries. The SQLite schema is intentionally compatible with the Python PostgresMemory adapter for migration.

Requirements

  • Node ≥ 22.5.0 (node:sqlite is experimental on Node 22.x, unflagged from 23.5+)
  • SQLite ≥ 3.35 (bundled with Node ≥ 22.5) for RETURNING and ON CONFLICT … DO UPDATE

Construction

import { SqliteMemory } from "@maniac-ai/agents/memory/sqlite";

// Adapter opens and owns the file
const memory = new SqliteMemory({ filename: "./agent.db" });

// Bring-your-own DatabaseSync — adapter never closes borrowed handles
const memory = new SqliteMemory({ database: existingHandle });
OptionDefaultPurpose
filenameFile path or :memory:
databaseExisting DatabaseSync handle (mutually exclusive with filename)
tablePrefix"maniac_memory"Table name prefix for multi-tenant databases
applyPragmastrueApply WAL, foreign_keys, and recommended sync settings in setup()

Schema

setup() creates:

CREATE TABLE maniac_memory (
  id         TEXT PRIMARY KEY,
  namespace  TEXT NOT NULL,
  content    TEXT NOT NULL,
  metadata   TEXT NOT NULL DEFAULT '{}',
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE INDEX maniac_memory_namespace_idx  ON maniac_memory(namespace);
CREATE INDEX maniac_memory_created_at_idx ON maniac_memory(created_at);

CREATE TABLE maniac_memory_sequences (
  namespace TEXT NOT NULL,
  key       TEXT NOT NULL,
  value     INTEGER NOT NULL,
  PRIMARY KEY (namespace, key)
);

content and metadata are JSON text; filter clauses use SQLite's json_extract.

Lifecycle

const memory = new SqliteMemory({ filename: "./agent.db" });
await memory.setup();
try {
  // use stores and Maniac app
} finally {
  await memory.close(); // no-op for borrowed database handles
}

SqliteMemory drops into every store: ConversationStore, ObservationStore, WorkingMemoryStore, RunCheckpointStore, and BackgroundTaskStore.

HonchoMemory

Wraps a base Memory adapter (default InMemoryMemory; production setups typically use SqliteMemory) and exposes the Honcho SDK for session context and Dialectic peer chat.

import { HonchoMemory } from "@maniac-ai/agents/memory/honcho";
import { SqliteMemory } from "@maniac-ai/agents/memory/sqlite";

const base = new SqliteMemory({ filename: "./agent.db" });
await base.setup();

const memory = new HonchoMemory({
  base,
  workspaceId: process.env.HONCHO_WORKSPACE_ID,
  environment: "production"
});

When Maniac detects HonchoMemory, it automatically:

  • Swaps ConversationStoreHonchoConversationStore (loads via session.context, mirrors writes)
  • Swaps WorkingMemoryStoreHonchoWorkingMemoryStore
  • Injects the ask_about_user built-in tool

@honcho-ai/sdk is an optional peer dependency. Pairing HonchoMemory with observationalMemory logs a warning — both paths compress history and can double-compact.

VectorMemory

A decorator over any base Memory. Canonical records stay in the base adapter; embeddings live in a sidecar VectorIndex.

import { VectorMemory, SqliteVectorIndex } from "@maniac-ai/agents/memory";
import { SqliteMemory } from "@maniac-ai/agents/memory/sqlite";

const sqlite = new SqliteMemory({ filename: "./agent.db" });
await sqlite.setup();

const memory = new VectorMemory({
  base: sqlite,
  embedder: myEmbedder, // async (text: string) => number[]
  index: new SqliteVectorIndex({ database: sqlite.database })
});

On save, text is embedded and upserted into the index. On search with MemoryQuery.text, the adapter runs k-nearest-neighbor lookup, then loads matching records from the base. If the embedder throws, search falls back to the base adapter's substring matching.

Shipped index implementations:

  • InMemoryVectorIndex — cosine similarity, suitable for tests and small workloads
  • SqliteVectorIndex — shares the SqliteMemory database handle

ObservationStore.searchObservations and searchReflections benefit automatically when the underlying Memory is wrapped.

Choosing an adapter

flowchart TD
  Start["Need persistence?"] -->|No| IM["InMemoryMemory"]
  Start -->|Yes| SQL["SqliteMemory"]
  SQL --> Honcho{"Honcho cloud?"}
  Honcho -->|Yes| HON["HonchoMemory wraps SqliteMemory"]
  Honcho -->|No| Vec{"Semantic search?"}
  Vec -->|Yes| VEC["VectorMemory wraps SqliteMemory"]
  Vec -->|No| SQL

On this page