@cuylabs/agent-memory
v4.10.0
Published
First-party memory provider for @cuylabs/agent-core
Readme
Agent Memory
First-party memory provider for @cuylabs/agent-core.
The package is organized by implementation area:
src/
file/
io.ts # low-level file reads and directory traversal
reader.ts # turns markdown artifacts and record files into records
provider.ts # default provider surface for agent-core
store.ts # file I/O and writable record operations
records.ts # record IDs and serialization
paths.ts # path defaults and resolution
turns.ts # connects agent-core turn hooks to provider-owned extraction
tools.ts # file adapter's memory tools
search/
bm25.ts # local BM25-style record search
tokenizer.ts # query and record tokenizationThe default provider is file-backed today, while the public factory stays storage-agnostic so recall can grow into hybrid file/vector/graph strategies without changing application code.
import { createAgent } from "@cuylabs/agent-core";
import { createMemoryProvider } from "@cuylabs/agent-memory";
const agent = createAgent({
model,
memory: createMemoryProvider({ root: ".agent-memory" }),
});Memory remains opt-in. @cuylabs/agent-core owns the provider contract and
lifecycle wiring; this package owns concrete provider adapters. There is no
YAML discovery layer in the default path: applications import a provider
factory, pass provider-specific options, and give the resulting provider to
createAgent({ memory }).
The file provider uses deterministic BM25-style file search for automatic
recall. Agent-core calls recall once per user turn by default, using the latest
user message as the query, then reuses that result across tool-loop steps. The
provider searches memory_summary.md, sections of MEMORY.md, and durable
records/*.md files, then returns only matching records to agent-core for
context-fragment injection.
The tokenizer is intentionally a small default, not a semantic memory model.
Callers can provide a custom tokenizer, stop words, or a different search
engine through createMemoryProvider({ recall: ... }) when the default BM25
behavior is not enough.
Pluggability model
The public integration point is the provider factory:
const memory = createMemoryProvider({
root: ".agent-memory",
recall: {
tokenizer: customTokenizer,
engine: customSearchEngine,
},
remember: {
extractTurn: customTurnExtractor,
},
});That keeps the hookup simple while still allowing the implementation to get more powerful. Recall, storage, and remembering can evolve behind the provider boundary without changing application code.
Turn Capture
agent-core calls provider lifecycle hooks when memory turn hooks are enabled
with memory.turns. This package keeps those low-level hook names internal and
exposes remember.extractTurn as the memory-facing option. src/file/turns.ts
adapts core onTurnEnd to the remember pipeline.
The default provider does not silently write every conversation turn into durable memory. Turn-end writes are enabled by passing a provider-owned extractor:
const memory = createMemoryProvider({
root: ".agent-memory",
remember: {
async extractTurn({ input, output }) {
if (!output) return [];
if (!input.includes("remember")) return [];
return {
title: "User preference",
content: output,
kind: "turn-extraction",
scope: "project",
tags: ["preference"],
};
},
},
});That extractor receives the full AgentMemoryTurnEndInput from core and
returns memory drafts. The file provider stores those drafts as normal records
with source: "turn_end", so recall and memory_search can find them on later
turns. If turns is disabled in createAgent({ memory: { ... } }), core does
not call these hooks.
No SQL database is required for the default provider. The current index is built in-process from local files on each search, which keeps installation portable and dependency-free for small to medium memory roots. A SQLite FTS or vector backend should be added behind the same recall/provider boundary when memory size or latency requires a persistent index.
Run the local file-provider example from this package with:
npx tsx examples/01-file-memory.ts