@waynesutton/agent-memory 0.0.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/.claude/settings.json +9 -0
- package/.claude/settings.local.json +7 -0
- package/AGENTS.md +113 -0
- package/CLAUDE.md +79 -0
- package/README.md +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +192 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/parsers/claude-code.d.ts +3 -0
- package/dist/cli/parsers/claude-code.d.ts.map +1 -0
- package/dist/cli/parsers/claude-code.js +75 -0
- package/dist/cli/parsers/claude-code.js.map +1 -0
- package/dist/cli/parsers/codex.d.ts +3 -0
- package/dist/cli/parsers/codex.d.ts.map +1 -0
- package/dist/cli/parsers/codex.js +42 -0
- package/dist/cli/parsers/codex.js.map +1 -0
- package/dist/cli/parsers/conductor.d.ts +3 -0
- package/dist/cli/parsers/conductor.d.ts.map +1 -0
- package/dist/cli/parsers/conductor.js +43 -0
- package/dist/cli/parsers/conductor.js.map +1 -0
- package/dist/cli/parsers/cursor.d.ts +3 -0
- package/dist/cli/parsers/cursor.d.ts.map +1 -0
- package/dist/cli/parsers/cursor.js +50 -0
- package/dist/cli/parsers/cursor.js.map +1 -0
- package/dist/cli/parsers/index.d.ts +12 -0
- package/dist/cli/parsers/index.d.ts.map +1 -0
- package/dist/cli/parsers/index.js +27 -0
- package/dist/cli/parsers/index.js.map +1 -0
- package/dist/cli/parsers/opencode.d.ts +3 -0
- package/dist/cli/parsers/opencode.d.ts.map +1 -0
- package/dist/cli/parsers/opencode.js +72 -0
- package/dist/cli/parsers/opencode.js.map +1 -0
- package/dist/cli/parsers/parsers.test.d.ts +2 -0
- package/dist/cli/parsers/parsers.test.d.ts.map +1 -0
- package/dist/cli/parsers/parsers.test.js +151 -0
- package/dist/cli/parsers/parsers.test.js.map +1 -0
- package/dist/cli/parsers/pi.d.ts +3 -0
- package/dist/cli/parsers/pi.d.ts.map +1 -0
- package/dist/cli/parsers/pi.js +43 -0
- package/dist/cli/parsers/pi.js.map +1 -0
- package/dist/cli/parsers/types.d.ts +25 -0
- package/dist/cli/parsers/types.d.ts.map +1 -0
- package/dist/cli/parsers/types.js +2 -0
- package/dist/cli/parsers/types.js.map +1 -0
- package/dist/cli/parsers/vscode-copilot.d.ts +3 -0
- package/dist/cli/parsers/vscode-copilot.d.ts.map +1 -0
- package/dist/cli/parsers/vscode-copilot.js +69 -0
- package/dist/cli/parsers/vscode-copilot.js.map +1 -0
- package/dist/cli/parsers/zed.d.ts +3 -0
- package/dist/cli/parsers/zed.d.ts.map +1 -0
- package/dist/cli/parsers/zed.js +43 -0
- package/dist/cli/parsers/zed.js.map +1 -0
- package/dist/cli/sync.d.ts +21 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +78 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/type-extractor.d.ts +25 -0
- package/dist/cli/type-extractor.d.ts.map +1 -0
- package/dist/cli/type-extractor.js +254 -0
- package/dist/cli/type-extractor.js.map +1 -0
- package/dist/cli/type-extractor.test.d.ts +2 -0
- package/dist/cli/type-extractor.test.d.ts.map +1 -0
- package/dist/cli/type-extractor.test.js +173 -0
- package/dist/cli/type-extractor.test.js.map +1 -0
- package/dist/client/http.d.ts +44 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +311 -0
- package/dist/client/http.js.map +1 -0
- package/dist/client/index.d.ts +158 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +256 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/_generated/api.d.ts +12 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +13 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +18 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +42 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +39 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/actions.d.ts +42 -0
- package/dist/component/actions.d.ts.map +1 -0
- package/dist/component/actions.js +405 -0
- package/dist/component/actions.js.map +1 -0
- package/dist/component/apiKeyMutations.d.ts +29 -0
- package/dist/component/apiKeyMutations.d.ts.map +1 -0
- package/dist/component/apiKeyMutations.js +149 -0
- package/dist/component/apiKeyMutations.js.map +1 -0
- package/dist/component/apiKeyQueries.d.ts +37 -0
- package/dist/component/apiKeyQueries.d.ts.map +1 -0
- package/dist/component/apiKeyQueries.js +127 -0
- package/dist/component/apiKeyQueries.js.map +1 -0
- package/dist/component/checksum.d.ts +6 -0
- package/dist/component/checksum.d.ts.map +1 -0
- package/dist/component/checksum.js +14 -0
- package/dist/component/checksum.js.map +1 -0
- package/dist/component/checksum.test.d.ts +2 -0
- package/dist/component/checksum.test.d.ts.map +1 -0
- package/dist/component/checksum.test.js +27 -0
- package/dist/component/checksum.test.js.map +1 -0
- package/dist/component/convex.config.d.ts +3 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +4 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/cronActions.d.ts +3 -0
- package/dist/component/cronActions.d.ts.map +1 -0
- package/dist/component/cronActions.js +38 -0
- package/dist/component/cronActions.js.map +1 -0
- package/dist/component/cronQueries.d.ts +6 -0
- package/dist/component/cronQueries.d.ts.map +1 -0
- package/dist/component/cronQueries.js +38 -0
- package/dist/component/cronQueries.js.map +1 -0
- package/dist/component/crons.d.ts +3 -0
- package/dist/component/crons.d.ts.map +1 -0
- package/dist/component/crons.js +18 -0
- package/dist/component/crons.js.map +1 -0
- package/dist/component/format.d.ts +11 -0
- package/dist/component/format.d.ts.map +1 -0
- package/dist/component/format.js +175 -0
- package/dist/component/format.js.map +1 -0
- package/dist/component/format.test.d.ts +2 -0
- package/dist/component/format.test.d.ts.map +1 -0
- package/dist/component/format.test.js +118 -0
- package/dist/component/format.test.js.map +1 -0
- package/dist/component/mutations.d.ts +158 -0
- package/dist/component/mutations.d.ts.map +1 -0
- package/dist/component/mutations.js +745 -0
- package/dist/component/mutations.js.map +1 -0
- package/dist/component/queries.d.ts +94 -0
- package/dist/component/queries.d.ts.map +1 -0
- package/dist/component/queries.js +574 -0
- package/dist/component/queries.js.map +1 -0
- package/dist/component/schema.d.ts +278 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +161 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +11 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +571 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/shared.d.ts +126 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +67 -0
- package/dist/shared.js.map +1 -0
- package/dist/test.d.ts +23 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +21 -0
- package/dist/test.js.map +1 -0
- package/eslint.config.js +15 -0
- package/example/convex/convex.config.ts +7 -0
- package/example/convex/memory.ts +129 -0
- package/llms.md +175 -0
- package/llms.txt +126 -0
- package/package.json +72 -0
- package/prds/API-REFERENCE.md +935 -0
- package/prds/README.md +988 -0
- package/prds/SETUP.md +682 -0
- package/src/cli/index.ts +254 -0
- package/src/cli/parsers/claude-code.ts +80 -0
- package/src/cli/parsers/codex.ts +45 -0
- package/src/cli/parsers/conductor.ts +47 -0
- package/src/cli/parsers/cursor.ts +55 -0
- package/src/cli/parsers/index.ts +30 -0
- package/src/cli/parsers/opencode.ts +84 -0
- package/src/cli/parsers/parsers.test.ts +201 -0
- package/src/cli/parsers/pi.ts +47 -0
- package/src/cli/parsers/types.ts +26 -0
- package/src/cli/parsers/vscode-copilot.ts +78 -0
- package/src/cli/parsers/zed.ts +47 -0
- package/src/cli/sync.ts +110 -0
- package/src/cli/type-extractor.test.ts +241 -0
- package/src/cli/type-extractor.ts +331 -0
- package/src/client/http.ts +415 -0
- package/src/client/index.ts +519 -0
- package/src/component/_generated/api.ts +14 -0
- package/src/component/_generated/dataModel.ts +20 -0
- package/src/component/_generated/server.ts +64 -0
- package/src/component/actions.ts +558 -0
- package/src/component/apiKeyMutations.ts +175 -0
- package/src/component/apiKeyQueries.ts +156 -0
- package/src/component/checksum.test.ts +31 -0
- package/src/component/checksum.ts +13 -0
- package/src/component/convex.config.ts +5 -0
- package/src/component/cronActions.ts +52 -0
- package/src/component/cronQueries.ts +42 -0
- package/src/component/crons.ts +34 -0
- package/src/component/format.test.ts +133 -0
- package/src/component/format.ts +232 -0
- package/src/component/mutations.ts +824 -0
- package/src/component/queries.ts +684 -0
- package/src/component/schema.ts +207 -0
- package/src/mcp/server.ts +695 -0
- package/src/shared.ts +251 -0
- package/src/test.ts +32 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +8 -0
package/prds/README.md
ADDED
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
# @waynesutton/agent-memory
|
|
2
|
+
|
|
3
|
+
A Convex Component for persistent, cloud-synced agent memory. Markdown-first memory backend for AI coding agents across CLIs and IDEs — with intelligent ingest, feedback loops, memory relations, and relevance decay.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
AI coding agents (Claude Code, Cursor, OpenCode, Codex, Conductor, Zed, VS Code Copilot, Pi) all use local, file-based memory systems (CLAUDE.md, .cursor/rules, AGENTS.md, etc.). These are siloed to one machine with no shared backend, no cross-tool sync, and no queryable search.
|
|
8
|
+
|
|
9
|
+
`@waynesutton/agent-memory` creates a cloud-synced, markdown-first memory backend as a Convex Component. It:
|
|
10
|
+
|
|
11
|
+
- Stores structured memories in Convex with full-text search and vector/semantic search
|
|
12
|
+
- **Intelligently ingests** raw conversations into deduplicated memories via LLM pipeline
|
|
13
|
+
- Exports memories in each tool's native format (`.cursor/rules/*.mdc`, `.claude/rules/*.md`, `AGENTS.md`, etc.)
|
|
14
|
+
- Syncs bidirectionally between local files and the cloud
|
|
15
|
+
- Tracks **memory history** (full audit trail of all changes)
|
|
16
|
+
- Supports **feedback loops** — agents rate memories as helpful or unhelpful
|
|
17
|
+
- Builds **memory relations** (graph connections between related memories)
|
|
18
|
+
- Applies **relevance decay** — stale, low-access memories lose priority over time
|
|
19
|
+
- Scopes memories by **agent, session, user, and project**
|
|
20
|
+
- Provides a **read-only HTTP API** with bearer token auth and rate limiting
|
|
21
|
+
- Exposes an MCP server with 14 tools for direct agent integration
|
|
22
|
+
- Works standalone with any Convex app — no dependency on `@convex-dev/agent`
|
|
23
|
+
|
|
24
|
+
## Table of Contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
|
+
- [Convex Component Setup](#convex-component-setup)
|
|
29
|
+
- [Client API](#client-api)
|
|
30
|
+
- [Intelligent Ingest](#intelligent-ingest)
|
|
31
|
+
- [Memory History](#memory-history)
|
|
32
|
+
- [Feedback & Scoring](#feedback--scoring)
|
|
33
|
+
- [Memory Relations](#memory-relations)
|
|
34
|
+
- [Relevance Decay](#relevance-decay)
|
|
35
|
+
- [CLI Usage](#cli-usage)
|
|
36
|
+
- [MCP Server](#mcp-server)
|
|
37
|
+
- [Schema](#schema)
|
|
38
|
+
- [Memory Types](#memory-types)
|
|
39
|
+
- [Tool Format Support](#tool-format-support)
|
|
40
|
+
- [Vector Search](#vector-search)
|
|
41
|
+
- [Read-Only HTTP API](#read-only-http-api)
|
|
42
|
+
- [Security](#security)
|
|
43
|
+
- [Architecture](#architecture)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @waynesutton/agent-memory
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Peer dependencies (must be installed in your Convex app):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install convex convex-helpers
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### 1. Add the component to your Convex app
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// convex/convex.config.ts
|
|
67
|
+
import { defineApp } from "convex/server";
|
|
68
|
+
import agentMemory from "@waynesutton/agent-memory/convex.config.js";
|
|
69
|
+
|
|
70
|
+
const app = defineApp();
|
|
71
|
+
app.use(agentMemory);
|
|
72
|
+
|
|
73
|
+
export default app;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2. Create wrapper functions
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// convex/memory.ts
|
|
80
|
+
import { v } from "convex/values";
|
|
81
|
+
import { query, mutation, action } from "./_generated/server.js";
|
|
82
|
+
import { components } from "./_generated/api.js";
|
|
83
|
+
import { AgentMemory } from "@waynesutton/agent-memory";
|
|
84
|
+
|
|
85
|
+
const memory = new AgentMemory(components.agentMemory, {
|
|
86
|
+
projectId: "my-project",
|
|
87
|
+
agentId: "my-app",
|
|
88
|
+
llmApiKey: process.env.OPENAI_API_KEY, // for intelligent ingest
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Save a memory
|
|
92
|
+
export const remember = mutation({
|
|
93
|
+
args: {
|
|
94
|
+
title: v.string(),
|
|
95
|
+
content: v.string(),
|
|
96
|
+
memoryType: v.union(
|
|
97
|
+
v.literal("instruction"),
|
|
98
|
+
v.literal("learning"),
|
|
99
|
+
v.literal("reference"),
|
|
100
|
+
v.literal("feedback"),
|
|
101
|
+
v.literal("journal"),
|
|
102
|
+
),
|
|
103
|
+
},
|
|
104
|
+
returns: v.string(),
|
|
105
|
+
handler: async (ctx, args) => memory.remember(ctx, args),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Search memories
|
|
109
|
+
export const recall = query({
|
|
110
|
+
args: { q: v.string() },
|
|
111
|
+
returns: v.any(),
|
|
112
|
+
handler: async (ctx, args) => memory.search(ctx, args.q),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// List all memories
|
|
116
|
+
export const listMemories = query({
|
|
117
|
+
args: {},
|
|
118
|
+
returns: v.any(),
|
|
119
|
+
handler: async (ctx) => memory.list(ctx),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Intelligently ingest raw text into memories
|
|
123
|
+
export const ingest = action({
|
|
124
|
+
args: { content: v.string() },
|
|
125
|
+
returns: v.any(),
|
|
126
|
+
handler: async (ctx, args) => memory.ingest(ctx, args.content),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// View change history
|
|
130
|
+
export const memoryHistory = query({
|
|
131
|
+
args: { memoryId: v.string() },
|
|
132
|
+
returns: v.any(),
|
|
133
|
+
handler: async (ctx, args) => memory.history(ctx, args.memoryId),
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 3. Deploy and start using
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npx convex dev
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Convex Component Setup
|
|
146
|
+
|
|
147
|
+
### Standalone (no other components)
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// convex/convex.config.ts
|
|
151
|
+
import { defineApp } from "convex/server";
|
|
152
|
+
import agentMemory from "@waynesutton/agent-memory/convex.config.js";
|
|
153
|
+
|
|
154
|
+
const app = defineApp();
|
|
155
|
+
app.use(agentMemory);
|
|
156
|
+
export default app;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Alongside @convex-dev/agent
|
|
160
|
+
|
|
161
|
+
Both components coexist independently with isolated tables:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// convex/convex.config.ts
|
|
165
|
+
import { defineApp } from "convex/server";
|
|
166
|
+
import agentMemory from "@waynesutton/agent-memory/convex.config.js";
|
|
167
|
+
import agent from "@convex-dev/agent/convex.config.js";
|
|
168
|
+
|
|
169
|
+
const app = defineApp();
|
|
170
|
+
app.use(agentMemory); // isolated tables for persistent memories
|
|
171
|
+
app.use(agent); // isolated tables for threads/messages
|
|
172
|
+
export default app;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
You can load memories into an Agent's system prompt:
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// convex/myAgent.ts
|
|
179
|
+
import { AgentMemory } from "@waynesutton/agent-memory";
|
|
180
|
+
import { Agent } from "@convex-dev/agent";
|
|
181
|
+
|
|
182
|
+
const memory = new AgentMemory(components.agentMemory, {
|
|
183
|
+
projectId: "my-app",
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export const chat = action({
|
|
187
|
+
args: { message: v.string() },
|
|
188
|
+
handler: async (ctx, args) => {
|
|
189
|
+
const bundle = await memory.getContextBundle(ctx);
|
|
190
|
+
const memoryContext = bundle.pinned
|
|
191
|
+
.map((m) => `## ${m.title}\n${m.content}`)
|
|
192
|
+
.join("\n\n");
|
|
193
|
+
|
|
194
|
+
const myAgent = new Agent(components.agent, {
|
|
195
|
+
model: "claude-sonnet-4-6",
|
|
196
|
+
instructions: `You are a helpful assistant.\n\nRelevant memories:\n${memoryContext}`,
|
|
197
|
+
});
|
|
198
|
+
// ... use agent as normal
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Client API
|
|
206
|
+
|
|
207
|
+
### Constructor
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { AgentMemory } from "@waynesutton/agent-memory";
|
|
211
|
+
|
|
212
|
+
const memory = new AgentMemory(components.agentMemory, {
|
|
213
|
+
projectId: "my-project", // required: unique project identifier
|
|
214
|
+
defaultScope: "project", // optional: "project" | "user" | "org"
|
|
215
|
+
userId: "user-123", // optional: for user-scoped memories
|
|
216
|
+
agentId: "claude-code", // optional: agent identifier
|
|
217
|
+
sessionId: "session-abc", // optional: session/conversation ID
|
|
218
|
+
embeddingApiKey: "sk-...", // optional: enables vector search
|
|
219
|
+
embeddingModel: "text-embedding-3-small", // optional
|
|
220
|
+
llmApiKey: "sk-...", // optional: enables intelligent ingest
|
|
221
|
+
llmModel: "gpt-4.1-nano", // optional: LLM for fact extraction
|
|
222
|
+
llmBaseUrl: "https://api.openai.com/v1", // optional: custom LLM endpoint
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Read Operations (query context)
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// List with rich filters
|
|
230
|
+
const all = await memory.list(ctx);
|
|
231
|
+
const byAgent = await memory.list(ctx, { agentId: "claude-code" });
|
|
232
|
+
const bySession = await memory.list(ctx, { sessionId: "session-123" });
|
|
233
|
+
const bySource = await memory.list(ctx, { source: "mcp" });
|
|
234
|
+
const byTags = await memory.list(ctx, { tags: ["api", "auth"] });
|
|
235
|
+
const recent = await memory.list(ctx, { createdAfter: Date.now() - 86400000 });
|
|
236
|
+
|
|
237
|
+
// Get a single memory
|
|
238
|
+
const mem = await memory.get(ctx, "jh72k...");
|
|
239
|
+
|
|
240
|
+
// Full-text search
|
|
241
|
+
const results = await memory.search(ctx, "API authentication");
|
|
242
|
+
|
|
243
|
+
// Progressive context bundle (feedback-boosted priority)
|
|
244
|
+
const bundle = await memory.getContextBundle(ctx, {
|
|
245
|
+
activePaths: ["src/api/routes.ts"],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Export as tool-native files
|
|
249
|
+
const files = await memory.exportForTool(ctx, "cursor");
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Write Operations (mutation context)
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Create (auto-records history)
|
|
256
|
+
const id = await memory.remember(ctx, {
|
|
257
|
+
title: "api-conventions",
|
|
258
|
+
content: "# API Conventions\n\n- Use camelCase\n- Return JSON",
|
|
259
|
+
memoryType: "instruction",
|
|
260
|
+
tags: ["api", "style"],
|
|
261
|
+
paths: ["src/api/**"],
|
|
262
|
+
priority: 0.9,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Update (auto-records history)
|
|
266
|
+
await memory.update(ctx, id, { content: "Updated content", priority: 1.0 });
|
|
267
|
+
|
|
268
|
+
// Archive & restore (both record history)
|
|
269
|
+
await memory.forget(ctx, id);
|
|
270
|
+
await memory.restore(ctx, id);
|
|
271
|
+
|
|
272
|
+
// Batch operations
|
|
273
|
+
await memory.batchArchive(ctx, ["id1", "id2", "id3"]);
|
|
274
|
+
await memory.batchUpdate(ctx, [
|
|
275
|
+
{ memoryId: "id1", priority: 0.9 },
|
|
276
|
+
{ memoryId: "id2", tags: ["updated"] },
|
|
277
|
+
]);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### History & Audit Trail (query context)
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// Get change history for a memory
|
|
284
|
+
const history = await memory.history(ctx, id);
|
|
285
|
+
// [{ event: "created", actor: "mcp", timestamp: ..., newContent: "..." }, ...]
|
|
286
|
+
|
|
287
|
+
// Get recent changes across the project
|
|
288
|
+
const recent = await memory.projectHistory(ctx, { limit: 20 });
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Feedback & Scoring (mutation/query context)
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Rate memories
|
|
295
|
+
await memory.addFeedback(ctx, id, "positive", { comment: "Very helpful rule" });
|
|
296
|
+
await memory.addFeedback(ctx, id, "negative", { comment: "Outdated information" });
|
|
297
|
+
|
|
298
|
+
// View feedback
|
|
299
|
+
const feedback = await memory.getFeedback(ctx, id);
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Memory Relations (mutation/query context)
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Create relationships
|
|
306
|
+
await memory.addRelation(ctx, memoryA, memoryB, "extends");
|
|
307
|
+
await memory.addRelation(ctx, memoryC, memoryA, "contradicts", { confidence: 0.9 });
|
|
308
|
+
|
|
309
|
+
// View relationships
|
|
310
|
+
const relations = await memory.getRelations(ctx, memoryA);
|
|
311
|
+
const contradictions = await memory.getRelations(ctx, memoryA, { relationship: "contradicts" });
|
|
312
|
+
|
|
313
|
+
// Remove a relationship
|
|
314
|
+
await memory.removeRelation(ctx, relationId);
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Access Tracking (mutation context)
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// Record that memories were read (for relevance decay)
|
|
321
|
+
await memory.recordAccess(ctx, ["id1", "id2"]);
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Embedding Operations (action context)
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// Single embedding
|
|
328
|
+
await memory.embed(ctx, id);
|
|
329
|
+
|
|
330
|
+
// Batch embed all un-embedded
|
|
331
|
+
const result = await memory.embedAll(ctx);
|
|
332
|
+
|
|
333
|
+
// Vector similarity search (falls back to full-text)
|
|
334
|
+
const results = await memory.semanticSearch(ctx, "how to handle auth errors");
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Intelligent Ingest
|
|
340
|
+
|
|
341
|
+
The core "smart memory" feature. Instead of manually creating memories, feed raw text and let the LLM pipeline:
|
|
342
|
+
|
|
343
|
+
1. **Extract** discrete facts/learnings from conversations or notes
|
|
344
|
+
2. **Search** existing memories for overlap (semantic deduplication)
|
|
345
|
+
3. **Decide** per-fact: ADD new, UPDATE existing, DELETE contradicted, or SKIP
|
|
346
|
+
4. **Return** a structured changelog of what happened
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const memory = new AgentMemory(components.agentMemory, {
|
|
350
|
+
projectId: "my-app",
|
|
351
|
+
llmApiKey: process.env.OPENAI_API_KEY,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// In an action context
|
|
355
|
+
const result = await memory.ingest(ctx,
|
|
356
|
+
`User prefers TypeScript strict mode. The API should use camelCase.
|
|
357
|
+
Actually, we switched from REST to GraphQL last week.
|
|
358
|
+
The old REST convention docs are outdated.`
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// result.results:
|
|
362
|
+
// [
|
|
363
|
+
// { event: "added", content: "User prefers TypeScript strict mode", memoryId: "..." },
|
|
364
|
+
// { event: "updated", content: "API uses camelCase with GraphQL", memoryId: "...", previousContent: "..." },
|
|
365
|
+
// { event: "deleted", content: "REST API conventions", memoryId: "...", previousContent: "..." },
|
|
366
|
+
// ]
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Custom Prompts
|
|
370
|
+
|
|
371
|
+
Override the extraction and decision prompts per-project:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
await memory.ingest(ctx, rawText, {
|
|
375
|
+
customExtractionPrompt: "Extract only coding conventions and preferences...",
|
|
376
|
+
customUpdatePrompt: "When facts conflict, always prefer the newer one...",
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Or set them in project settings for all ingest operations:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
await ctx.runMutation(components.agentMemory.mutations.upsertProject, {
|
|
384
|
+
projectId: "my-app",
|
|
385
|
+
name: "My App",
|
|
386
|
+
settings: {
|
|
387
|
+
autoSync: false,
|
|
388
|
+
syncFormats: [],
|
|
389
|
+
factExtractionPrompt: "Your custom extraction prompt...",
|
|
390
|
+
updateDecisionPrompt: "Your custom update decision prompt...",
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Memory History
|
|
398
|
+
|
|
399
|
+
Every create, update, archive, restore, and merge operation records a history entry. This provides a complete audit trail of how memories change over time.
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
const history = await memory.history(ctx, memoryId);
|
|
403
|
+
// Returns: MemoryHistoryEntry[]
|
|
404
|
+
// Each entry has: event, actor, timestamp, previousContent, newContent
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
History is automatically cleaned up by a weekly cron job (entries older than 90 days are removed).
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Feedback & Scoring
|
|
412
|
+
|
|
413
|
+
Agents and users can rate memories. Feedback affects the **effective priority** used in context bundles:
|
|
414
|
+
|
|
415
|
+
- Each `positive` feedback adds up to +0.05 priority (max +0.2 boost)
|
|
416
|
+
- Each `negative` feedback subtracts up to -0.1 priority (max -0.5 penalty)
|
|
417
|
+
- `very_negative` counts as negative with stronger signal
|
|
418
|
+
|
|
419
|
+
This means good memories naturally float to the top of context bundles, while bad ones sink — without manual priority management.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Memory Relations
|
|
424
|
+
|
|
425
|
+
Build a knowledge graph between memories:
|
|
426
|
+
|
|
427
|
+
| Relationship | Meaning |
|
|
428
|
+
|-------------|---------|
|
|
429
|
+
| `extends` | Memory B adds detail to Memory A |
|
|
430
|
+
| `contradicts` | Memory B conflicts with Memory A |
|
|
431
|
+
| `replaces` | Memory B supersedes Memory A |
|
|
432
|
+
| `related_to` | General association |
|
|
433
|
+
|
|
434
|
+
Relations are directional (`from` -> `to`) and queryable by direction and type.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Relevance Decay
|
|
439
|
+
|
|
440
|
+
Memories that aren't accessed lose priority over time, preventing stale memories from dominating context windows.
|
|
441
|
+
|
|
442
|
+
**How it works:**
|
|
443
|
+
- A daily cron job (3 AM UTC) checks all non-pinned memories
|
|
444
|
+
- Memories with low access count and old `lastAccessedAt` get reduced priority
|
|
445
|
+
- Decay follows an exponential half-life (configurable per-project, default 30 days)
|
|
446
|
+
- Pinned memories (priority >= 0.8) are never decayed
|
|
447
|
+
|
|
448
|
+
**Enable per-project:**
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
await ctx.runMutation(components.agentMemory.mutations.upsertProject, {
|
|
452
|
+
projectId: "my-app",
|
|
453
|
+
name: "My App",
|
|
454
|
+
settings: {
|
|
455
|
+
autoSync: false,
|
|
456
|
+
syncFormats: [],
|
|
457
|
+
decayEnabled: true,
|
|
458
|
+
decayHalfLifeDays: 30, // memories lose half their priority every 30 days of no access
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## CLI Usage
|
|
466
|
+
|
|
467
|
+
The CLI syncs memories between local tool files and Convex.
|
|
468
|
+
|
|
469
|
+
### Environment Variable
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
export CONVEX_URL="https://your-deployment.convex.cloud"
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Commands
|
|
476
|
+
|
|
477
|
+
#### `npx agent-memory init`
|
|
478
|
+
|
|
479
|
+
Detect tools in the current directory and register the project.
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
npx agent-memory init --project my-app --name "My App"
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
Detects: Claude Code, Cursor, OpenCode, Codex, Conductor, Zed, VS Code Copilot, Pi.
|
|
486
|
+
|
|
487
|
+
#### `npx agent-memory push`
|
|
488
|
+
|
|
489
|
+
Push local memory files to Convex.
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
npx agent-memory push --project my-app
|
|
493
|
+
npx agent-memory push --project my-app --format claude-code # specific tool only
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### `npx agent-memory pull`
|
|
497
|
+
|
|
498
|
+
Pull memories from Convex to local files.
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
npx agent-memory pull --project my-app --format cursor
|
|
502
|
+
npx agent-memory pull --project my-app --format claude-code
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### `npx agent-memory list`
|
|
506
|
+
|
|
507
|
+
List all memories in the terminal.
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
npx agent-memory list --project my-app
|
|
511
|
+
npx agent-memory list --project my-app --type instruction
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### `npx agent-memory search <query>`
|
|
515
|
+
|
|
516
|
+
Search memories from the terminal.
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
npx agent-memory search "API conventions" --project my-app --limit 5
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### `npx agent-memory mcp`
|
|
523
|
+
|
|
524
|
+
Start the MCP server (see [MCP Server](#mcp-server) section).
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
npx agent-memory mcp --project my-app
|
|
528
|
+
npx agent-memory mcp --project my-app --llm-api-key $OPENAI_API_KEY # enable ingest
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Hook Integration
|
|
532
|
+
|
|
533
|
+
Auto-sync on Claude Code session start/end:
|
|
534
|
+
|
|
535
|
+
```json
|
|
536
|
+
// .claude/settings.json
|
|
537
|
+
{
|
|
538
|
+
"hooks": {
|
|
539
|
+
"SessionStart": [{
|
|
540
|
+
"hooks": [{ "type": "command", "command": "npx agent-memory pull --format claude-code" }]
|
|
541
|
+
}],
|
|
542
|
+
"SessionEnd": [{
|
|
543
|
+
"hooks": [{ "type": "command", "command": "npx agent-memory push --format claude-code" }]
|
|
544
|
+
}]
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## MCP Server
|
|
552
|
+
|
|
553
|
+
The MCP server runs as a local process, bridging AI tools to your Convex backend via stdio/JSON-RPC.
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
┌─────────────┐ stdio/JSON-RPC ┌──────────────────┐ ConvexHttpClient ┌─────────┐
|
|
557
|
+
│ Claude Code │ <────────────────> │ MCP Server │ <──────────────────> │ Convex │
|
|
558
|
+
│ Cursor │ │ (local process) │ │ Cloud │
|
|
559
|
+
│ VS Code │ │ npx agent-memory │ │ │
|
|
560
|
+
└─────────────┘ └──────────────────┘ └─────────┘
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Starting the Server
|
|
564
|
+
|
|
565
|
+
```bash
|
|
566
|
+
npx agent-memory mcp --project my-app
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Options:
|
|
570
|
+
| Flag | Description |
|
|
571
|
+
|------|-------------|
|
|
572
|
+
| `--project <id>` | Project ID (default: "default") |
|
|
573
|
+
| `--read-only` | Disable write operations |
|
|
574
|
+
| `--disable-tools <tools>` | Comma-separated tool names to disable |
|
|
575
|
+
| `--embedding-api-key <key>` | Enable vector search |
|
|
576
|
+
| `--llm-api-key <key>` | Enable intelligent ingest |
|
|
577
|
+
| `--llm-model <model>` | LLM model for ingest (default: "gpt-4.1-nano") |
|
|
578
|
+
|
|
579
|
+
### MCP Tools (14 total)
|
|
580
|
+
|
|
581
|
+
| Tool | Description |
|
|
582
|
+
|------|-------------|
|
|
583
|
+
| `memory_remember` | Save a new memory (with agent/session scoping) |
|
|
584
|
+
| `memory_recall` | Search memories by keyword (full-text) |
|
|
585
|
+
| `memory_semantic_recall` | Search memories by meaning (vector) |
|
|
586
|
+
| `memory_list` | List memories with filters (agent, session, source, tags) |
|
|
587
|
+
| `memory_context` | Get context bundle (pinned + relevant) |
|
|
588
|
+
| `memory_forget` | Archive a memory |
|
|
589
|
+
| `memory_restore` | Restore an archived memory |
|
|
590
|
+
| `memory_update` | Update an existing memory |
|
|
591
|
+
| `memory_history` | View change audit trail |
|
|
592
|
+
| `memory_feedback` | Rate a memory as helpful/unhelpful |
|
|
593
|
+
| `memory_relate` | Create relationship between memories |
|
|
594
|
+
| `memory_relations` | View memory graph connections |
|
|
595
|
+
| `memory_batch_archive` | Archive multiple memories at once |
|
|
596
|
+
| `memory_ingest` | Intelligently extract memories from raw text |
|
|
597
|
+
|
|
598
|
+
### MCP Resources
|
|
599
|
+
|
|
600
|
+
| URI | Description |
|
|
601
|
+
|-----|-------------|
|
|
602
|
+
| `memory://project/{id}/pinned` | High-priority memories auto-loaded at session start |
|
|
603
|
+
|
|
604
|
+
### Configuration in Claude Code
|
|
605
|
+
|
|
606
|
+
```json
|
|
607
|
+
// .claude/settings.json
|
|
608
|
+
{
|
|
609
|
+
"mcpServers": {
|
|
610
|
+
"agent-memory": {
|
|
611
|
+
"command": "npx",
|
|
612
|
+
"args": [
|
|
613
|
+
"agent-memory", "mcp",
|
|
614
|
+
"--project", "my-app",
|
|
615
|
+
"--llm-api-key", "${env:OPENAI_API_KEY}"
|
|
616
|
+
],
|
|
617
|
+
"env": {
|
|
618
|
+
"CONVEX_URL": "${env:CONVEX_URL}",
|
|
619
|
+
"OPENAI_API_KEY": "${env:OPENAI_API_KEY}"
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Configuration in Cursor
|
|
627
|
+
|
|
628
|
+
```json
|
|
629
|
+
// .cursor/mcp.json
|
|
630
|
+
{
|
|
631
|
+
"mcpServers": {
|
|
632
|
+
"agent-memory": {
|
|
633
|
+
"command": "npx",
|
|
634
|
+
"args": [
|
|
635
|
+
"agent-memory", "mcp",
|
|
636
|
+
"--project", "my-app",
|
|
637
|
+
"--llm-api-key", "${env:OPENAI_API_KEY}"
|
|
638
|
+
],
|
|
639
|
+
"env": {
|
|
640
|
+
"CONVEX_URL": "${env:CONVEX_URL}",
|
|
641
|
+
"OPENAI_API_KEY": "${env:OPENAI_API_KEY}"
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Schema
|
|
651
|
+
|
|
652
|
+
The component creates 7 isolated tables in your Convex deployment:
|
|
653
|
+
|
|
654
|
+
### `memories`
|
|
655
|
+
|
|
656
|
+
| Field | Type | Description |
|
|
657
|
+
|-------|------|-------------|
|
|
658
|
+
| `projectId` | `string` | Project identifier |
|
|
659
|
+
| `scope` | `"project" \| "user" \| "org"` | Visibility scope |
|
|
660
|
+
| `userId` | `string?` | Owner for user-scoped memories |
|
|
661
|
+
| `agentId` | `string?` | Agent that created/owns this memory |
|
|
662
|
+
| `sessionId` | `string?` | Session/conversation ID |
|
|
663
|
+
| `title` | `string` | Short title/slug |
|
|
664
|
+
| `content` | `string` | Markdown content |
|
|
665
|
+
| `memoryType` | `MemoryType` | Category (see below) |
|
|
666
|
+
| `tags` | `string[]` | Searchable tags |
|
|
667
|
+
| `paths` | `string[]?` | File glob patterns for relevance matching |
|
|
668
|
+
| `priority` | `number?` | 0-1 scale (>= 0.8 = pinned) |
|
|
669
|
+
| `source` | `string?` | Origin tool ("claude-code", "cursor", "mcp", "ingest", etc.) |
|
|
670
|
+
| `checksum` | `string` | FNV-1a hash for change detection |
|
|
671
|
+
| `archived` | `boolean` | Soft delete flag |
|
|
672
|
+
| `embeddingId` | `Id?` | Link to vector embedding |
|
|
673
|
+
| `accessCount` | `number?` | Times this memory was accessed |
|
|
674
|
+
| `lastAccessedAt` | `number?` | Timestamp of last access |
|
|
675
|
+
| `positiveCount` | `number?` | Positive feedback count |
|
|
676
|
+
| `negativeCount` | `number?` | Negative feedback count |
|
|
677
|
+
|
|
678
|
+
**Indexes:** `by_project`, `by_project_scope`, `by_project_title`, `by_type_priority`, `by_agent`, `by_session`, `by_source`, `by_last_accessed`
|
|
679
|
+
**Search indexes:** `search_content` (full-text on content), `search_title` (full-text on title)
|
|
680
|
+
|
|
681
|
+
### `embeddings`
|
|
682
|
+
|
|
683
|
+
Vector embeddings for semantic search. Linked to memories via `memoryId`.
|
|
684
|
+
|
|
685
|
+
**Vector index:** `by_embedding` (1536 dimensions, OpenAI-compatible)
|
|
686
|
+
|
|
687
|
+
### `projects`
|
|
688
|
+
|
|
689
|
+
Project registry with settings for sync, custom prompts, and decay configuration.
|
|
690
|
+
|
|
691
|
+
### `syncLog`
|
|
692
|
+
|
|
693
|
+
Tracks push/pull sync events for conflict detection.
|
|
694
|
+
|
|
695
|
+
### `memoryHistory`
|
|
696
|
+
|
|
697
|
+
Audit trail of all memory changes: created, updated, archived, restored, merged.
|
|
698
|
+
|
|
699
|
+
### `memoryFeedback`
|
|
700
|
+
|
|
701
|
+
Quality signals from agents/users: positive, negative, very_negative with optional comments.
|
|
702
|
+
|
|
703
|
+
### `memoryRelations`
|
|
704
|
+
|
|
705
|
+
Directional graph connections between memories with relationship types and metadata.
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## Memory Types
|
|
710
|
+
|
|
711
|
+
| Type | Description | Maps To |
|
|
712
|
+
|------|-------------|---------|
|
|
713
|
+
| `instruction` | Rules and conventions | `.claude/rules/`, `.cursor/rules/`, `AGENTS.md` |
|
|
714
|
+
| `learning` | Auto-discovered patterns | Claude Code auto-memory |
|
|
715
|
+
| `reference` | Architecture docs, API specs | Reference documentation |
|
|
716
|
+
| `feedback` | Corrections, preferences | User feedback on behavior |
|
|
717
|
+
| `journal` | Session logs | OpenCode journal entries |
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## Tool Format Support
|
|
722
|
+
|
|
723
|
+
The component reads from and writes to 8 tool formats:
|
|
724
|
+
|
|
725
|
+
| Tool | Parser Reads | Formatter Writes |
|
|
726
|
+
|------|-------------|-----------------|
|
|
727
|
+
| **Claude Code** | `.claude/rules/*.md` (YAML frontmatter) | `.claude/rules/<title>.md` |
|
|
728
|
+
| **Cursor** | `.cursor/rules/*.mdc` (YAML frontmatter) | `.cursor/rules/<title>.mdc` |
|
|
729
|
+
| **OpenCode** | `AGENTS.md` (## sections) | `AGENTS.md` or `journal/<title>.md` |
|
|
730
|
+
| **Codex** | `AGENTS.md`, `AGENTS.override.md` | `AGENTS.md` or `<dir>/AGENTS.md` |
|
|
731
|
+
| **Conductor** | `.conductor/rules/*.md` | `.conductor/rules/<title>.md` |
|
|
732
|
+
| **Zed** | `.zed/rules/*.md` | `.zed/rules/<title>.md` |
|
|
733
|
+
| **VS Code Copilot** | `.github/copilot-instructions.md`, `.copilot/rules/*.md` | `.github/copilot-instructions.md` |
|
|
734
|
+
| **Pi** | `.pi/rules/*.md` | `.pi/rules/<title>.md` |
|
|
735
|
+
|
|
736
|
+
### Cross-Tool Sync Example
|
|
737
|
+
|
|
738
|
+
Push from Claude Code, pull as Cursor rules:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
# In your project directory with .claude/rules/ files
|
|
742
|
+
npx agent-memory push --project my-app --format claude-code
|
|
743
|
+
|
|
744
|
+
# On another machine or for another tool
|
|
745
|
+
npx agent-memory pull --project my-app --format cursor
|
|
746
|
+
# Creates .cursor/rules/*.mdc files with proper frontmatter
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
---
|
|
750
|
+
|
|
751
|
+
## Vector Search
|
|
752
|
+
|
|
753
|
+
Vector search is opt-in. Everything works with full-text search by default.
|
|
754
|
+
|
|
755
|
+
### Enabling Vector Search
|
|
756
|
+
|
|
757
|
+
Pass an OpenAI API key when configuring:
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
const memory = new AgentMemory(components.agentMemory, {
|
|
761
|
+
projectId: "my-app",
|
|
762
|
+
embeddingApiKey: process.env.OPENAI_API_KEY,
|
|
763
|
+
embeddingModel: "text-embedding-3-small", // default
|
|
764
|
+
});
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Or via CLI/MCP:
|
|
768
|
+
|
|
769
|
+
```bash
|
|
770
|
+
npx agent-memory mcp --project my-app --embedding-api-key $OPENAI_API_KEY
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Backfilling Embeddings
|
|
774
|
+
|
|
775
|
+
```typescript
|
|
776
|
+
// In an action context
|
|
777
|
+
const result = await memory.embedAll(ctx);
|
|
778
|
+
console.log(`Embedded ${result.embedded} memories, skipped ${result.skipped}`);
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Fallback Behavior
|
|
782
|
+
|
|
783
|
+
If no embedding API key is provided, `semanticSearch` automatically falls back to full-text search. No errors, no configuration changes needed.
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
## Read-Only HTTP API
|
|
788
|
+
|
|
789
|
+
Expose memories as REST endpoints for dashboards, CI/CD, and external integrations. The component provides a `MemoryHttpApi` class that generates `httpAction` handlers — your app mounts them on its own `httpRouter`.
|
|
790
|
+
|
|
791
|
+
### Setup
|
|
792
|
+
|
|
793
|
+
```typescript
|
|
794
|
+
// convex/http.ts
|
|
795
|
+
import { httpRouter } from "convex/server";
|
|
796
|
+
import { MemoryHttpApi } from "@waynesutton/agent-memory/http";
|
|
797
|
+
import { components } from "./_generated/api";
|
|
798
|
+
|
|
799
|
+
const http = httpRouter();
|
|
800
|
+
const memoryApi = new MemoryHttpApi(components.agentMemory, {
|
|
801
|
+
corsOrigins: ["https://myapp.com"], // optional, defaults to ["*"]
|
|
802
|
+
});
|
|
803
|
+
memoryApi.mount(http, "/api/memory");
|
|
804
|
+
export default http;
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Creating API Keys
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
// convex/memory.ts — behind your app's own auth
|
|
811
|
+
import { AgentMemory } from "@waynesutton/agent-memory";
|
|
812
|
+
|
|
813
|
+
const memory = new AgentMemory(components.agentMemory, { projectId: "my-app" });
|
|
814
|
+
|
|
815
|
+
export const createReadKey = mutation({
|
|
816
|
+
args: {},
|
|
817
|
+
handler: async (ctx) => {
|
|
818
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
819
|
+
if (!identity) throw new Error("Not authenticated");
|
|
820
|
+
return await memory.createApiKey(ctx, {
|
|
821
|
+
name: "Dashboard key",
|
|
822
|
+
permissions: ["list", "search", "context"],
|
|
823
|
+
});
|
|
824
|
+
},
|
|
825
|
+
});
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### Using the API
|
|
829
|
+
|
|
830
|
+
```bash
|
|
831
|
+
# List memories
|
|
832
|
+
curl -H "Authorization: Bearer am_<key>" \
|
|
833
|
+
https://your-deployment.convex.cloud/api/memory/list
|
|
834
|
+
|
|
835
|
+
# Search
|
|
836
|
+
curl -H "Authorization: Bearer am_<key>" \
|
|
837
|
+
"https://your-deployment.convex.cloud/api/memory/search?q=API+conventions"
|
|
838
|
+
|
|
839
|
+
# Get context bundle
|
|
840
|
+
curl -H "Authorization: Bearer am_<key>" \
|
|
841
|
+
https://your-deployment.convex.cloud/api/memory/context
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### Endpoints
|
|
845
|
+
|
|
846
|
+
| Path | Permission | Description |
|
|
847
|
+
|------|------------|-------------|
|
|
848
|
+
| `/list` | `list` | List memories with filters |
|
|
849
|
+
| `/get?id=<id>` | `get` | Get single memory |
|
|
850
|
+
| `/search?q=<query>` | `search` | Full-text search |
|
|
851
|
+
| `/context` | `context` | Progressive context bundle |
|
|
852
|
+
| `/export?format=<format>` | `export` | Export in tool format |
|
|
853
|
+
| `/history?id=<id>` | `history` | Memory audit trail |
|
|
854
|
+
| `/relations?id=<id>` | `relations` | Memory graph |
|
|
855
|
+
|
|
856
|
+
### Rate Limiting
|
|
857
|
+
|
|
858
|
+
Self-contained fixed-window token bucket (no external dependency):
|
|
859
|
+
|
|
860
|
+
- **Default:** 100 requests per 60 seconds
|
|
861
|
+
- **Configurable:** per-key override > per-project setting > global default
|
|
862
|
+
- Returns `429` with `retryAfterMs` when exceeded
|
|
863
|
+
- Old window records cleaned up hourly by cron
|
|
864
|
+
|
|
865
|
+
### Available Permissions
|
|
866
|
+
|
|
867
|
+
`list`, `get`, `search`, `context`, `export`, `history`, `relations`
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## Security
|
|
872
|
+
|
|
873
|
+
6 layers of protection:
|
|
874
|
+
|
|
875
|
+
| Layer | What | How |
|
|
876
|
+
|-------|------|-----|
|
|
877
|
+
| **Deployment URL** | Gate to your Convex backend | `CONVEX_URL` env var required. Each app has its own isolated deployment. |
|
|
878
|
+
| **Auth Token** | Authenticates the caller | Optional `CONVEX_AUTH_TOKEN` for production/team use. |
|
|
879
|
+
| **API Keys** | HTTP API access control | Bearer tokens with per-key permissions, expiry, and rate limits. Keys stored as hashes. |
|
|
880
|
+
| **Project Scope** | Isolates by project | `--project` flag. MCP server and API keys only access that project's memories. |
|
|
881
|
+
| **Tool Disabling** | Restrict operations | `--read-only` or `--disable-tools` flags for fine-grained control. |
|
|
882
|
+
| **Convex Isolation** | Runtime sandboxing | Component tables are isolated. Queries can't write. Mutations are transactional. |
|
|
883
|
+
|
|
884
|
+
### Examples
|
|
885
|
+
|
|
886
|
+
```bash
|
|
887
|
+
# Full access (default)
|
|
888
|
+
npx agent-memory mcp --project my-app
|
|
889
|
+
|
|
890
|
+
# Read-only (no write/delete/ingest tools)
|
|
891
|
+
npx agent-memory mcp --project my-app --read-only
|
|
892
|
+
|
|
893
|
+
# Disable specific tools
|
|
894
|
+
npx agent-memory mcp --project my-app --disable-tools memory_forget,memory_ingest
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### MCP Config with Secrets
|
|
898
|
+
|
|
899
|
+
```json
|
|
900
|
+
{
|
|
901
|
+
"mcpServers": {
|
|
902
|
+
"agent-memory": {
|
|
903
|
+
"command": "npx",
|
|
904
|
+
"args": ["agent-memory", "mcp", "--project", "my-app"],
|
|
905
|
+
"env": {
|
|
906
|
+
"CONVEX_URL": "${env:CONVEX_URL}",
|
|
907
|
+
"CONVEX_AUTH_TOKEN": "${env:CONVEX_AUTH_TOKEN}"
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
916
|
+
## Architecture
|
|
917
|
+
|
|
918
|
+
```
|
|
919
|
+
@waynesutton/agent-memory
|
|
920
|
+
├── src/
|
|
921
|
+
│ ├── component/ # Convex backend (defineComponent)
|
|
922
|
+
│ │ ├── schema.ts # 9 tables: memories, embeddings, projects, syncLog,
|
|
923
|
+
│ │ │ # memoryHistory, memoryFeedback, memoryRelations,
|
|
924
|
+
│ │ │ # apiKeys, rateLimitTokens
|
|
925
|
+
│ │ ├── mutations.ts # CRUD + batch + feedback + relations + history tracking
|
|
926
|
+
│ │ ├── queries.ts # list, search, context bundle, history, feedback, relations
|
|
927
|
+
│ │ ├── actions.ts # embeddings, semantic search, intelligent ingest pipeline
|
|
928
|
+
│ │ ├── apiKeyMutations.ts # API key create/revoke, rate limit consume
|
|
929
|
+
│ │ ├── apiKeyQueries.ts # API key validation, listing
|
|
930
|
+
│ │ ├── crons.ts # Daily relevance decay + weekly history cleanup + hourly rate limit cleanup
|
|
931
|
+
│ │ ├── cronActions.ts # Internal actions called by cron jobs
|
|
932
|
+
│ │ ├── cronQueries.ts # Internal queries for cron job data
|
|
933
|
+
│ │ ├── format.ts # Memory -> tool-native file conversion
|
|
934
|
+
│ │ └── checksum.ts # FNV-1a content hashing
|
|
935
|
+
│ ├── client/
|
|
936
|
+
│ │ ├── index.ts # AgentMemory class (public API)
|
|
937
|
+
│ │ └── http.ts # MemoryHttpApi class (read-only HTTP API)
|
|
938
|
+
│ ├── cli/
|
|
939
|
+
│ │ ├── index.ts # CLI: init, push, pull, list, search, mcp
|
|
940
|
+
│ │ ├── sync.ts # Push/pull sync logic
|
|
941
|
+
│ │ └── parsers/ # 8 tool parsers (local files -> memories)
|
|
942
|
+
│ ├── mcp/
|
|
943
|
+
│ │ └── server.ts # MCP server (14 tools + resources)
|
|
944
|
+
│ ├── shared.ts # Shared types and validators
|
|
945
|
+
│ └── test.ts # Test helper for convex-test
|
|
946
|
+
└── example/
|
|
947
|
+
└── convex/ # Example Convex app
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
### Key Design Principles
|
|
951
|
+
|
|
952
|
+
- **Works without any API key** — full-text search, CRUD, sync, export, history, feedback, and relations all work with zero external dependencies
|
|
953
|
+
- **Vector search is opt-in** — pass `embeddingApiKey` to enable; falls back to full-text automatically
|
|
954
|
+
- **Intelligent ingest is opt-in** — pass `llmApiKey` to enable LLM-powered fact extraction and deduplication
|
|
955
|
+
- **Standalone** — no dependency on `@convex-dev/agent` or any other component
|
|
956
|
+
- **Markdown-first** — memories are markdown documents with optional YAML frontmatter
|
|
957
|
+
- **Checksum-based sync** — only changed content is pushed/pulled (FNV-1a hashing)
|
|
958
|
+
- **Progressive disclosure** — context bundles tier memories as pinned/relevant/available
|
|
959
|
+
- **Feedback-boosted scoring** — positive feedback raises priority; negative feedback lowers it
|
|
960
|
+
- **Self-maintaining** — cron jobs handle relevance decay and history cleanup automatically
|
|
961
|
+
- **Multi-dimensional scoping** — project + user + agent + session isolation
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
## Testing
|
|
966
|
+
|
|
967
|
+
### Using in Your Tests
|
|
968
|
+
|
|
969
|
+
```typescript
|
|
970
|
+
import { convexTest } from "convex-test";
|
|
971
|
+
import agentMemoryTest from "@waynesutton/agent-memory/test";
|
|
972
|
+
|
|
973
|
+
const t = convexTest();
|
|
974
|
+
agentMemoryTest.register(t);
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
### Running Component Tests
|
|
978
|
+
|
|
979
|
+
```bash
|
|
980
|
+
npm test # run all tests
|
|
981
|
+
npm run test:watch # watch mode
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
## License
|
|
987
|
+
|
|
988
|
+
Apache-2.0
|