@memstack/core 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,11 @@
1
1
  # MemStack
2
2
 
3
- > Implementation priority is maintained in the [canonical roadmap](docs/ROADMAP.md).
4
-
5
3
  > The open-source memory layer for AI agents — store, retrieve, summarize, and prune.
6
4
 
7
5
  [![npm version](https://img.shields.io/npm/v/@memstack/core)](https://www.npmjs.com/package/@memstack/core)
8
6
  [![skills.sh](https://skills.sh/b/isiomaC/memstack)](https://skills.sh/isiomaC/memstack)
9
7
  [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-%40memstack%2Fmcp-blueviolet)](https://registry.modelcontextprotocol.io/?q=io.github.isiomaC%2Fmemstack)
8
+ [![memstack MCP server – quality and maintenance score on Glama](https://glama.ai/mcp/servers/isiomaC/memstack/badges/score.svg)](https://glama.ai/mcp/servers/isiomaC/memstack)
10
9
  [![CI](https://github.com/isiomaC/memstack/actions/workflows/ci.yml/badge.svg)](https://github.com/isiomaC/memstack/actions/workflows/ci.yml)
11
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
11
  [![MCP Reference](https://img.shields.io/badge/MCP-LLM%20Reference-blue)](https://gitmcp.io/isiomaC/memstack)
@@ -15,11 +14,11 @@
15
14
  # Use MemStack in your application
16
15
  npm install @memstack/core
17
16
 
18
- # Give your coding agent the MemStack skill
19
- npx skills add isiomaC/memstack
17
+ # Give your coding agents the MemStack skill (-g: every project; omit it for this project only)
18
+ npx skills add isiomaC/memstack -g
20
19
  ```
21
20
 
22
- `@memstack/core` is the runtime SDK; the Agent Skill teaches compatible coding agents how to integrate and operate MemStack correctly.
21
+ `@memstack/core` is the runtime SDK; the Agent Skill teaches compatible coding agents how to integrate and operate MemStack correctly. Without `-g`, the skill installs into the current project (`.agents/skills/`, plus a `.claude/skills/` link for Claude Code). Update it later with `npx skills update`.
23
22
 
24
23
  **The problem:** AI agents forget. Every interaction starts from zero. You either stuff everything into the context window (expensive, slow, degrades output quality) or the agent has no memory of past conversations.
25
24
 
@@ -27,12 +26,21 @@ npx skills add isiomaC/memstack
27
26
 
28
27
  Think of it as the open-source alternative to [Mem0](https://mem0.ai/) — pluggable storage, bring your own LLM, zero vendor lock-in.
29
28
 
29
+ [![memstack MCP server – quality and maintenance score on Glama](https://glama.ai/mcp/servers/isiomaC/memstack/badges/card.svg)](https://glama.ai/mcp/servers/isiomaC/memstack)
30
+
30
31
  ---
31
32
 
32
33
  ## Table of Contents
33
34
 
34
35
  - [Why MemStack](#why-memstack)
35
36
  - [Quick Start](#quick-start)
37
+ - [Harness Memory (Claude Code & Codex)](#harness-memory-claude-code--codex)
38
+ - [How it works](#how-it-works)
39
+ - [Commands](#commands)
40
+ - [What `connect` changes](#what-connect-changes)
41
+ - [Projects](#projects)
42
+ - [Storage](#storage)
43
+ - [Troubleshooting](#troubleshooting)
36
44
  - [The Memory Pipeline](#the-memory-pipeline)
37
45
  - [Store](#1-store)
38
46
  - [Retrieve](#2-retrieve)
@@ -55,7 +63,9 @@ Think of it as the open-source alternative to [Mem0](https://mem0.ai/) — plugg
55
63
  - [Memory Subsystem](#memory-subsystem)
56
64
  - [Export / Import](#export-import)
57
65
  - [Health & Close](#health-close)
66
+ - [Harness Memory API](#harness-memory-api)
58
67
  - [Configuration](#configuration)
68
+ - [Harness configuration file](#harness-configuration-file)
59
69
  - [Advanced Usage](#advanced-usage)
60
70
  - [Custom Storage](#custom-storage)
61
71
  - [Custom LLM / Embedding](#custom-llm-embedding)
@@ -94,6 +104,24 @@ Think of it as the open-source alternative to [Mem0](https://mem0.ai/) — plugg
94
104
 
95
105
  ## Quick Start
96
106
 
107
+ ### Claude Code and Codex
108
+
109
+ Persistent memory across agent harnesses: what you tell Claude Code, Codex
110
+ recalls in the same project, and the reverse.
111
+
112
+ ```bash
113
+ npm install -g @memstack/cli @memstack/mcp better-sqlite3@^11.10.0 # MemStack never installs storage drivers for you
114
+ memstack init # choose an LLM provider and a store
115
+ memstack connect claude-code
116
+ memstack connect codex
117
+ ```
118
+
119
+ Then, in Claude Code: "Remember that this project uses Hono." In Codex, in the
120
+ same repository: "What framework does this project use?" Codex answers Hono.
121
+ See [Harness Memory](#harness-memory-claude-code--codex) for how it works.
122
+
123
+ ### As a library
124
+
97
125
  ```bash
98
126
  npm install @memstack/core
99
127
  ```
@@ -122,7 +150,7 @@ import { MemStack, OpenAILLMAdapter, InMemoryStorageAdapter } from "@memstack/co
122
150
  const llm = new OpenAILLMAdapter({
123
151
  apiKey: process.env.DEEPSEEK_API_KEY!,
124
152
  baseURL: "https://api.deepseek.com/v1",
125
- defaultModel: "deepseek-chat",
153
+ defaultModel: "deepseek-flash",
126
154
  });
127
155
 
128
156
  const memstack = new MemStack({
@@ -197,6 +225,113 @@ console.log(response.text);
197
225
 
198
226
  ---
199
227
 
228
+ ## Harness Memory (Claude Code & Codex)
229
+
230
+ Coding agents forget everything between sessions, and they don't share what
231
+ they learn with each other. MemStack gives Claude Code and Codex one memory
232
+ per project: a decision made with one agent is known to the other, and to
233
+ every later session.
234
+
235
+ ### How it works
236
+
237
+ - **Saving.** Each agent gets five MemStack tools (`memory_store`,
238
+ `memory_retrieve`, `memory_get`, `memory_delete`, `memory_stats`) through
239
+ the MCP harness profile. When you say "remember that…", or the agent learns
240
+ a durable fact, decision, preference, or rule, it calls `memory_store`.
241
+ MemStack asks your LLM for a few topic tags, so "uses Hono" can later be
242
+ found by "which framework?". If tagging fails, the memory is still saved.
243
+ - **Recalling.** A session-start hook loads the project's most important
244
+ memories into every new session, so the agent starts out knowing them.
245
+ During a session the agent calls `memory_retrieve` with plain questions.
246
+ Recall runs locally with keyword ranking (BM25 with stemming) and never
247
+ calls the LLM, so it is fast and works offline.
248
+ - **Scope.** Memories belong to the current project. Preferences that apply
249
+ everywhere can be saved as global (`scope: "global"`) and are recalled in
250
+ every project. One project can never read or delete another's memories.
251
+ - **Safety.** Agents don't get bulk or destructive tools, secrets are never to
252
+ be stored (the agents are told so), and the LLM key stays in
253
+ `~/.memstack/config.json` (readable only by you), never in agent configs.
254
+
255
+ ### Commands
256
+
257
+ | Command | What it does |
258
+ |---|---|
259
+ | `memstack init` | Choose an LLM provider and a store. Verifies the key with a real request and writes `~/.memstack/config.json`. Non-interactive: `--provider`, `--base-url`, `--model`, `--api-key-env <VAR>`, `--store`, `--path`, `--url`, `--yes`. |
260
+ | `memstack connect <claude-code\|codex>` | Registers MemStack with the agent. Checks the server works first, and undoes everything if a step fails. `--dry-run` shows the changes; `--no-hooks` and `--no-agents-md` skip those parts. |
261
+ | `memstack disconnect <claude-code\|codex>` | Removes everything `connect` added. Your memories are kept. |
262
+ | `memstack status` | Config, storage, the current project, and what each agent has connected. |
263
+ | `memstack doctor` | Diagnoses setup problems and prints the fix for each. `--live` also tests the LLM key. |
264
+ | `memstack memories [query]` | Lists or searches this project's memories. `--global` for global ones, `--delete <id>` to remove one. |
265
+ | `memstack project` | Shows this repository's project ID. `project pin <id>` fixes it in `.memstack.json`; `project merge <old-id>` moves memories from an old ID. |
266
+
267
+ ### What `connect` changes
268
+
269
+ `connect` changes only these entries, backs up each file first, and
270
+ `disconnect` restores each file exactly:
271
+
272
+ | Agent | File | Change |
273
+ |---|---|---|
274
+ | Claude Code | `~/.claude.json` | A user-scope `memstack` MCP server, added with `claude mcp add-json`. |
275
+ | Claude Code | `~/.claude/settings.json` | A `SessionStart` hook running `memstack-mcp hook session-start`. |
276
+ | Codex | `~/.codex/config.toml` | A `memstack` MCP server, added with `codex mcp add`. |
277
+ | Codex | `~/.codex/hooks.json` | A `SessionStart` hook. Codex runs it after you approve it once with `/hooks`. |
278
+ | Codex | `~/.codex/AGENTS.md` | A short block between `memstack:begin`/`memstack:end` markers telling Codex to save memories with `memory_store`. |
279
+
280
+ The registered command is the `memstack-mcp` you installed, run by absolute
281
+ path, so it works even when the agent starts without your shell's `PATH`.
282
+ Restart Claude Code, or start a new Codex session, to load it.
283
+
284
+ ### Projects
285
+
286
+ The project ID comes from the repository itself, with nothing stored, so it
287
+ stays the same across clones, worktrees, renamed remotes, moved folders, new
288
+ machines, and storage switches:
289
+
290
+ 1. a pinned ID in `.memstack.json` at the repository root
291
+ (`memstack project pin <id>`; commit it to share it with your team);
292
+ 2. otherwise, the repository's first commit;
293
+ 3. for shallow clones, the `origin` remote;
294
+ 4. for repositories without commits, the git directory, and outside git, the
295
+ folder. Memories saved before a repository's first commit move to the new
296
+ ID automatically.
297
+
298
+ A fork shares its first commit with the original repository, so on one store
299
+ the two share memories until one of them runs `memstack project pin`.
300
+
301
+ ### Storage
302
+
303
+ Any supported store works; pick it in `memstack init`. MemStack never installs
304
+ storage drivers: install the one you need alongside `@memstack/mcp`.
305
+
306
+ | Store | Driver to install | Notes |
307
+ |---|---|---|
308
+ | `sqlite` | `better-sqlite3@^11.10.0` | Default; one local file, safe for both agents at once. |
309
+ | `postgres` | `postgres@^3.4.9` | Shared across machines. |
310
+ | `redis` | `ioredis@^5.11.1` | |
311
+ | `disk`, `markdown` | none | Single process only; `doctor` warns when both agents use them. |
312
+
313
+ Switching stores keeps project IDs; move memories with `memstack export` and
314
+ `memstack import`.
315
+
316
+ ### Troubleshooting
317
+
318
+ Run `memstack doctor` first; it checks the config file and its permissions,
319
+ the LLM key, the storage driver, each agent's registration, hook, and
320
+ guidance, and starts the server to confirm it answers.
321
+
322
+ - **"memstack-mcp is not installed" or a missing driver:** run the
323
+ `npm install -g …` command it prints.
324
+ - **Codex doesn't load memories at session start:** approve the MemStack hook
325
+ in Codex with `/hooks`.
326
+ - **Codex doesn't save when asked:** check `memstack status` shows the
327
+ `AGENTS.md` guidance as present; reconnect if not.
328
+ - **Memories seem missing:** `memstack project` shows which project you are
329
+ in. Use `memstack project merge <old-id>` to bring memories from an old ID.
330
+
331
+ Details on the MCP server itself: [docs/MCP_SETUP.md](docs/MCP_SETUP.md#harness-profile-claude-code-and-codex).
332
+
333
+ ---
334
+
200
335
  ## The Memory Pipeline
201
336
 
202
337
  MemStack's core is a five-stage pipeline. Each stage can be used independently.
@@ -209,7 +344,7 @@ Every agent interaction becomes a `Memory` with metadata that controls how it's
209
344
  interface Memory {
210
345
  id: string;
211
346
  actorId: string; // Who this memory belongs to (user ID, agent ID, session ID)
212
- memoryType: MemoryType; // "interaction" | "summary" | "observation" | "fact" | "reflection"
347
+ memoryType: MemoryType; // "interaction" | "summary" | "observation" | "fact" | "reflection" | "preference" | "decision" | "instruction"
213
348
  content: string; // The actual text
214
349
  importance: number; // 0-1 — higher = survives pruning, ranks higher in retrieval
215
350
  emotionalValence: number; // -1 to 1 — for tone-aware retrieval
@@ -505,6 +640,9 @@ const userCount = await ms.memory.count({ actorId: "user-42" });
505
640
  | `observation` | Passive knowledge — facts, documents, things the agent knows but didn't interact with. | "Company refund policy is 30 days from purchase." |
506
641
  | `fact` | Verified knowledge — discrete truths the agent has confirmed. | "The user's subscription tier is Enterprise." |
507
642
  | `reflection` | Self-generated insight — the agent thinking about its own experiences. | "I tend to over-explain billing policies — should be more concise." |
643
+ | `preference` | How the user likes things done. | "Prefer small pull requests with one concern each." |
644
+ | `decision` | A choice made, ideally with its reason. | "Chose Hono over Express for edge runtime support." |
645
+ | `instruction` | A standing rule to follow. | "Never commit directly to main." |
508
646
 
509
647
  Types control retrieval behavior — `compileContext()` treats `interaction` and `summary` differently from `observation`. Use types to separate "what happened" from "what I know."
510
648
 
@@ -534,6 +672,29 @@ await ms.memory.retrieve({ actorId: "x", query: "login bug", strategy: "hybrid"
534
672
  - Use `semantic` for RAG, document search, knowledge base queries
535
673
  - Use `hybrid` for most agent memory — it balances meaning with significance
536
674
 
675
+ ### Keyword recall on any storage adapter
676
+
677
+ `LexicalRetriever` answers natural questions without embeddings or an LLM
678
+ call, and behaves the same on every storage adapter. It loads the memories
679
+ in the given actors through `retrieve()`, then ranks them in MemStack with
680
+ BM25 over content and tags, with stemming and prefix matching. When nothing
681
+ matches, it returns the most important memories instead.
682
+
683
+ ```typescript
684
+ import { LexicalRetriever } from "@memstack/core";
685
+
686
+ const retriever = new LexicalRetriever(storage);
687
+ const { hits, fallback } = await retriever.recall({
688
+ actorIds: ["project:abc", "global"], // Searched together
689
+ query: "What framework does this project use?",
690
+ limit: 10, // Max results
691
+ maxChars: 8000, // Max total content; the top hit is always returned
692
+ });
693
+ ```
694
+
695
+ Up to 2,000 memories per actor are ranked (`candidateLimit`). Only returned
696
+ memories are marked as accessed.
697
+
537
698
  ---
538
699
 
539
700
  ## Embeddings
@@ -896,6 +1057,39 @@ const status = await ms.health();
896
1057
  await ms.close(); // graceful shutdown
897
1058
  ```
898
1059
 
1060
+ ### Harness Memory API
1061
+
1062
+ The harness features are built on `HarnessMemory`, which works with any
1063
+ storage adapter. Use it to build your own agent integration:
1064
+
1065
+ ```typescript
1066
+ import { HarnessMemory, defaultRecallNamespaces, projectNamespace } from "@memstack/core";
1067
+
1068
+ const memory = new HarnessMemory({ storage, llm });
1069
+
1070
+ await memory.remember({
1071
+ namespace: projectNamespace("acme-api"), // or "global"
1072
+ content: "This project uses Hono",
1073
+ kind: "decision", // fact | preference | decision | instruction | observation | ...
1074
+ source: { harness: "my-agent", project: "acme-api" },
1075
+ });
1076
+
1077
+ const { hits, fallback } = await memory.recall({
1078
+ namespaces: defaultRecallNamespaces("acme-api"), // project, then global
1079
+ query: "Which framework do we use?",
1080
+ limit: 10,
1081
+ maxChars: 8000,
1082
+ });
1083
+
1084
+ await memory.get(id, namespaces); // null outside namespaces
1085
+ await memory.forget(id, namespaces); // refuses ids outside namespaces
1086
+ await memory.stats(namespaces); // { "project:acme-api": 3, global: 1 }
1087
+ await memory.moveNamespace(from, to); // e.g. when a project's ID changes
1088
+ ```
1089
+
1090
+ `remember` asks the LLM for topic tags (disable with `autoTags: false`); every
1091
+ other method makes no LLM call. `recall` uses [`LexicalRetriever`](#keyword-recall-on-any-storage-adapter).
1092
+
899
1093
  ---
900
1094
 
901
1095
  ## Configuration
@@ -927,6 +1121,26 @@ const ms = new MemStack({
927
1121
  });
928
1122
  ```
929
1123
 
1124
+ ### Harness configuration file
1125
+
1126
+ The CLI and the MCP harness profile read `~/.memstack/config.json` (or
1127
+ `$MEMSTACK_HOME/config.json`), which `memstack init` writes with permissions
1128
+ readable only by you:
1129
+
1130
+ ```json
1131
+ {
1132
+ "version": 1,
1133
+ "llm": { "provider": "openai-compatible", "apiKey": "…", "baseURL": "https://api.deepseek.com", "model": "deepseek-flash" },
1134
+ "storage": { "type": "sqlite", "path": "/Users/you/.memstack/memstack.db" }
1135
+ }
1136
+ ```
1137
+
1138
+ Environment variables override the file one section at a time: any LLM
1139
+ variable (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `MEMSTACK_OPENAI_BASE_URL`,
1140
+ `MEMSTACK_LLM_MODEL`) replaces the whole `llm` section, and `MEMSTACK_STORAGE`
1141
+ replaces the whole `storage` section, so a key is never sent to another
1142
+ provider's URL.
1143
+
930
1144
  ---
931
1145
 
932
1146
  ## Advanced Usage
@@ -935,6 +1149,19 @@ const ms = new MemStack({
935
1149
 
936
1150
  Implement `StorageProvider` for any database. The interface is 9 methods. See the reference section above for the full contract.
937
1151
 
1152
+ Optional members, none of them required:
1153
+
1154
+ - `capabilities: { multiProcess?, textSearch? }` declares whether several
1155
+ processes can share the store safely and whether `search()` is native.
1156
+ - `search(query)` provides native full-text search. `LexicalRetriever` uses
1157
+ it when `textSearch` is declared and ranks memories itself otherwise.
1158
+ - `retrieve()` should honor `touch: false` by returning memories without
1159
+ marking them as accessed.
1160
+
1161
+ `SQLiteStorageAdapter` enables WAL and a 5-second busy timeout so several
1162
+ processes can share one database file. Set `walMode: false` or
1163
+ `busyTimeoutMs` to change this.
1164
+
938
1165
  ### Custom LLM / Embedding
939
1166
 
940
1167
  Implement `LLMProvider` or `EmbeddingProvider` for any service: