@cogmem/engram 0.0.0 → 0.1.0

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
@@ -167,7 +167,7 @@ Add to your MCP client configuration (e.g., Claude Code `settings.json`):
167
167
  "mcpServers": {
168
168
  "engram": {
169
169
  "command": "bunx",
170
- "args": ["@cogmem/engram-mcp"]
170
+ "args": ["-p", "@cogmem/engram", "engram-mcp"]
171
171
  }
172
172
  }
173
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogmem/engram",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Human memory for artificial minds — a cognitive memory system modeled on neuroscience",
5
5
  "type": "module",
6
6
  "exports": {
@@ -31,6 +31,13 @@ export function recall(
31
31
  candidateMap.set(m.id, m);
32
32
  }
33
33
 
34
+ if (options?.context) {
35
+ const contextMatches = storage.getMemoriesByContext(options.context, options?.type, limit * 2);
36
+ for (const m of contextMatches) {
37
+ candidateMap.set(m.id, m);
38
+ }
39
+ }
40
+
34
41
  const allCandidates = options?.type
35
42
  ? storage.getAllMemories(options.type)
36
43
  : storage.getAllMemories();
package/src/mcp/server.ts CHANGED
@@ -17,11 +17,7 @@ server.registerTool(
17
17
  "memory_store",
18
18
  {
19
19
  title: "Store Memory",
20
- description: `Store memories. Requires "action" field.
21
-
22
- Actions:
23
- - action:"encode" — Create a new memory. Params: content (required), type? (semantic|episodic|procedural, default semantic), emotion? (neutral|curiosity|surprise|anxiety|satisfaction|frustration|confusion|excitement|calm|urgency), emotionWeight? (0-1), context? (e.g. "project:acme")
24
- - action:"reconsolidate" — Update an existing memory during recall. Params: id (required), newContext?, currentEmotion?, currentEmotionWeight?`,
20
+ description: `Actions: encode(content) — store memory | encode_batch(memories[]) — store multiple | reconsolidate(id) — update during recall. Optional: type, emotion, emotionWeight, context.`,
25
21
  inputSchema: z.discriminatedUnion("action", [
26
22
  z.object({
27
23
  action: z.literal("encode"),
@@ -31,6 +27,22 @@ Actions:
31
27
  emotionWeight: z.number().min(0).max(1).optional().describe("Emotion intensity 0-1"),
32
28
  context: z.string().optional().describe("Context tag (e.g. project:acme)"),
33
29
  }),
30
+ z.object({
31
+ action: z.literal("encode_batch"),
32
+ memories: z
33
+ .array(
34
+ z.object({
35
+ content: z.string(),
36
+ type: z.nativeEnum(MemoryType).optional(),
37
+ emotion: z.nativeEnum(Emotion).optional(),
38
+ emotionWeight: z.number().min(0).max(1).optional(),
39
+ context: z.string().optional(),
40
+ }),
41
+ )
42
+ .min(1)
43
+ .max(50)
44
+ .describe("Array of memories to encode"),
45
+ }),
34
46
  z.object({
35
47
  action: z.literal("reconsolidate"),
36
48
  id: z.string().describe("Memory ID to update"),
@@ -47,12 +59,7 @@ server.registerTool(
47
59
  "memory_recall",
48
60
  {
49
61
  title: "Recall Memories",
50
- description: `Retrieve memories. Requires "action" field.
51
-
52
- Actions:
53
- - action:"recall" — Cue-based retrieval with spreading activation. Params: cue (required), limit? (default 5), type? (semantic|episodic|procedural), context?, associative? (default true), verbose?
54
- - action:"inspect" — Examine a specific memory's full lifecycle. Params: id (required, full ID or prefix)
55
- - action:"stats" — Memory system overview (counts, working memory usage, last consolidation). No extra params.`,
62
+ description: `Actions: recall(cue) — cue-based retrieval | list — browse without activation effects | inspect(id) — full lifecycle | stats — system overview. Optional: limit, type, context, format, verbose.`,
56
63
  inputSchema: z.discriminatedUnion("action", [
57
64
  z.object({
58
65
  action: z.literal("recall").optional().default("recall"),
@@ -62,11 +69,20 @@ Actions:
62
69
  context: z.string().optional().describe("Filter by context"),
63
70
  associative: z.boolean().optional().describe("Spreading activation (default: true)"),
64
71
  verbose: z.boolean().optional().describe("Full fields"),
72
+ format: z.enum(["full", "content", "ids"]).optional().describe("Response format (default: full)"),
65
73
  }),
66
74
  z.object({
67
75
  action: z.literal("inspect"),
68
76
  id: z.string().describe("Memory ID or prefix"),
69
77
  }),
78
+ z.object({
79
+ action: z.literal("list"),
80
+ type: z.nativeEnum(MemoryType).optional().describe("Filter by type"),
81
+ context: z.string().optional().describe("Filter by context prefix"),
82
+ limit: z.number().optional().describe("Max results (default: 20)"),
83
+ offset: z.number().optional().describe("Skip first N results (default: 0)"),
84
+ format: z.enum(["full", "content", "ids"]).optional().describe("Response format (default: full)"),
85
+ }),
70
86
  z.object({
71
87
  action: z.literal("stats"),
72
88
  }),
@@ -79,14 +95,7 @@ server.registerTool(
79
95
  "memory_manage",
80
96
  {
81
97
  title: "Manage Memory",
82
- description: `Memory maintenance. Requires "action" field.
83
-
84
- Actions:
85
- - action:"consolidate" — Run sleep cycle (strengthen, prune, extract patterns, discover links). No extra params.
86
- - action:"focus_push" — Push to working memory buffer. Params: content (required), memoryRef?
87
- - action:"focus_pop" — Remove most recent item from working memory. No extra params.
88
- - action:"focus_get" — View current working memory contents. No extra params.
89
- - action:"focus_clear" — Clear all working memory. No extra params.`,
98
+ description: `Actions: consolidate run sleep cycle | recall_to_focus(cue) — recall and load to working memory | focus_push(content) — push to buffer | focus_pop — pop newest | focus_get — view buffer | focus_clear — empty buffer.`,
90
99
  inputSchema: z.discriminatedUnion("action", [
91
100
  z.object({
92
101
  action: z.literal("consolidate"),
@@ -105,6 +114,13 @@ Actions:
105
114
  z.object({
106
115
  action: z.literal("focus_clear"),
107
116
  }),
117
+ z.object({
118
+ action: z.literal("recall_to_focus"),
119
+ cue: z.string().describe("Recall cue"),
120
+ limit: z.number().optional().describe("Max memories to load (default: 3)"),
121
+ type: z.nativeEnum(MemoryType).optional().describe("Filter by type"),
122
+ context: z.string().optional().describe("Filter by context"),
123
+ }),
108
124
  ]),
109
125
  },
110
126
  async (args) => handleManage(engine, args),
package/src/mcp/tools.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { CognitiveConfig } from "../config/defaults.ts";
2
2
  import type { EngramStorage } from "../storage/sqlite.ts";
3
3
  import type { EngramEngine } from "../core/engine.ts";
4
- import { isValidMemoryType } from "../core/memory.ts";
4
+ import { isValidMemoryType, MemoryType, Emotion } from "../core/memory.ts";
5
5
  import { encode } from "../core/encoder.ts";
6
6
  import { recall } from "../core/recall.ts";
7
7
  import {
@@ -38,6 +38,8 @@ export function handleStore(
38
38
  switch (args.action) {
39
39
  case "encode":
40
40
  return handleEncode(engine.storage, engine.config, args as any, engine.projectContext);
41
+ case "encode_batch":
42
+ return handleEncodeBatch(engine.storage, engine.config, args as any, engine.projectContext);
41
43
  case "reconsolidate":
42
44
  return handleReconsolidate(engine.storage, engine.config, args as any);
43
45
  default:
@@ -55,6 +57,8 @@ export function handleRecall(
55
57
  return handleRecallQuery(engine.storage, engine.config, args as any, engine.projectContext);
56
58
  case "inspect":
57
59
  return handleInspect(engine.storage, args as any);
60
+ case "list":
61
+ return handleList(engine.storage, args as any, engine.projectContext);
58
62
  case "stats":
59
63
  return handleStats(engine.storage, engine.config);
60
64
  default:
@@ -77,6 +81,8 @@ export function handleManage(
77
81
  return handleFocusGet(engine.storage, engine.config);
78
82
  case "focus_clear":
79
83
  return handleFocusClear(engine.storage);
84
+ case "recall_to_focus":
85
+ return handleRecallToFocus(engine.storage, engine.config, args as any, engine.projectContext);
80
86
  default:
81
87
  return errorResult(`Unknown manage action: ${args.action}`);
82
88
  }
@@ -96,12 +102,12 @@ function handleEncode(
96
102
  ): ToolResult {
97
103
  const typeStr = args.type ?? "semantic";
98
104
  if (!isValidMemoryType(typeStr)) {
99
- return errorResult(`Invalid type: ${typeStr}. Must be episodic, semantic, or procedural.`);
105
+ return errorResult(`Invalid type '${typeStr}'. Valid: ${Object.values(MemoryType).join(", ")}`);
100
106
  }
101
107
 
102
108
  const emotionStr = args.emotion ?? "neutral";
103
109
  if (!isValidEmotion(emotionStr)) {
104
- return errorResult(`Invalid emotion: ${emotionStr}.`);
110
+ return errorResult(`Invalid emotion '${emotionStr}'. Valid: ${Object.values(Emotion).join(", ")}`);
105
111
  }
106
112
 
107
113
  const memory = encode(
@@ -116,17 +122,58 @@ function handleEncode(
116
122
  config,
117
123
  );
118
124
 
119
- return textResult(
120
- JSON.stringify({
121
- id: memory.id,
122
- type: memory.type,
123
- content: memory.content,
124
- activation: memory.activation,
125
- emotion: memory.emotion,
126
- emotionWeight: memory.emotionWeight,
127
- context: memory.context,
128
- }),
129
- );
125
+ return textResult(JSON.stringify({ id: memory.id }));
126
+ }
127
+
128
+ function handleEncodeBatch(
129
+ storage: EngramStorage,
130
+ config: CognitiveConfig,
131
+ args: {
132
+ memories: Array<{
133
+ content: string;
134
+ type?: string;
135
+ emotion?: string;
136
+ emotionWeight?: number;
137
+ context?: string;
138
+ }>;
139
+ },
140
+ defaultContext?: string | null,
141
+ ): ToolResult {
142
+ const ids: string[] = [];
143
+ const errors: string[] = [];
144
+
145
+ storage.transaction(() => {
146
+ for (let i = 0; i < args.memories.length; i++) {
147
+ const m = args.memories[i]!;
148
+ const typeStr = m.type ?? "semantic";
149
+ if (!isValidMemoryType(typeStr)) {
150
+ errors.push(`[${i}] Invalid type '${typeStr}'`);
151
+ continue;
152
+ }
153
+ const emotionStr = m.emotion ?? "neutral";
154
+ if (!isValidEmotion(emotionStr)) {
155
+ errors.push(`[${i}] Invalid emotion '${emotionStr}'`);
156
+ continue;
157
+ }
158
+ const memory = encode(
159
+ storage,
160
+ {
161
+ content: m.content,
162
+ type: typeStr,
163
+ emotion: emotionStr,
164
+ emotionWeight: m.emotionWeight,
165
+ context: m.context ?? defaultContext ?? undefined,
166
+ },
167
+ config,
168
+ );
169
+ ids.push(memory.id);
170
+ }
171
+ });
172
+
173
+ if (errors.length > 0) {
174
+ return textResult(JSON.stringify({ stored: ids, errors }));
175
+ }
176
+ return textResult(JSON.stringify({ stored: ids }));
130
177
  }
131
178
 
132
179
  function handleRecallQuery(
@@ -139,6 +186,7 @@ function handleRecallQuery(
139
186
  context?: string;
140
187
  associative?: boolean;
141
188
  verbose?: boolean;
189
+ format?: "full" | "content" | "ids";
142
190
  },
143
191
  defaultContext?: string | null,
144
192
  ): ToolResult {
@@ -155,6 +203,15 @@ function handleRecallQuery(
155
203
  return textResult("No memories found above retrieval threshold.");
156
204
  }
157
205
 
206
+ const format = args.format ?? "full";
207
+
208
+ if (format === "ids") {
209
+ return textResult(JSON.stringify(results.map((r) => r.memory.id)));
210
+ }
211
+ if (format === "content") {
212
+ return textResult(JSON.stringify(results.map((r) => r.memory.content)));
213
+ }
214
+
158
215
  const formatted = args.verbose
159
216
  ? results.map((r) => ({
160
217
  id: r.memory.id,
@@ -178,6 +235,56 @@ function handleRecallQuery(
178
235
  return textResult(JSON.stringify(formatted));
179
236
  }
180
237
 
238
+ function handleList(
239
+ storage: EngramStorage,
240
+ args: {
241
+ type?: string;
242
+ context?: string;
243
+ limit?: number;
244
+ offset?: number;
245
+ format?: "full" | "content" | "ids";
246
+ },
247
+ defaultContext?: string | null,
248
+ ): ToolResult {
249
+ const limit = args.limit ?? 20;
250
+ const offset = args.offset ?? 0;
251
+ const typeFilter = args.type && isValidMemoryType(args.type) ? args.type : undefined;
252
+ const context = args.context ?? defaultContext ?? undefined;
253
+
254
+ let results;
255
+ if (context) {
256
+ results = storage.getMemoriesByContext(context, typeFilter, limit + offset);
257
+ } else {
258
+ results = storage.getAllMemories(typeFilter).slice(0, limit + offset);
259
+ }
260
+ results = results.slice(offset, offset + limit);
261
+
262
+ if (results.length === 0) {
263
+ return textResult("No memories found.");
264
+ }
265
+
266
+ const format = args.format ?? "full";
267
+
268
+ if (format === "ids") {
269
+ return textResult(JSON.stringify(results.map((m) => m.id)));
270
+ }
271
+ if (format === "content") {
272
+ return textResult(JSON.stringify(results.map((m) => m.content)));
273
+ }
274
+
275
+ return textResult(
276
+ JSON.stringify(
277
+ results.map((m) => ({
278
+ id: m.id,
279
+ content: m.content,
280
+ type: m.type,
281
+ context: m.context,
282
+ activation: m.activation,
283
+ })),
284
+ ),
285
+ );
286
+ }
287
+
181
288
  function handleFocusPush(
182
289
  storage: EngramStorage,
183
290
  config: CognitiveConfig,
@@ -222,6 +329,34 @@ function handleFocusClear(storage: EngramStorage): ToolResult {
222
329
  return textResult(`Cleared ${count} items from working memory.`);
223
330
  }
224
331
 
332
+ function handleRecallToFocus(
333
+ storage: EngramStorage,
334
+ config: CognitiveConfig,
335
+ args: { cue: string; limit?: number; type?: string; context?: string },
336
+ defaultContext?: string | null,
337
+ ): ToolResult {
338
+ const typeFilter = args.type && isValidMemoryType(args.type) ? args.type : undefined;
339
+ const limit = args.limit ?? 3;
340
+
341
+ const results = recall(storage, args.cue, config, {
342
+ limit,
343
+ type: typeFilter,
344
+ context: args.context ?? defaultContext ?? undefined,
345
+ associative: true,
346
+ });
347
+
348
+ const loaded: string[] = [];
349
+ for (const r of results) {
350
+ pushFocus(storage, r.memory.content, config, {
351
+ memoryRef: r.memory.id,
352
+ });
353
+ loaded.push(r.memory.id);
354
+ }
355
+
356
+ const { used, capacity } = focusUtilization(storage, config);
357
+ return textResult(JSON.stringify({ loaded, focus: { used, capacity } }));
358
+ }
359
+
225
360
  function handleConsolidate(storage: EngramStorage, config: CognitiveConfig): ToolResult {
226
361
  const result = consolidate(storage, config);
227
362
  const chunks = discoverChunks(storage, config);
@@ -324,9 +459,6 @@ function handleReconsolidate(
324
459
  return textResult(
325
460
  JSON.stringify({
326
461
  id: updated.id,
327
- content: updated.content,
328
- emotion: updated.emotion,
329
- emotionWeight: updated.emotionWeight,
330
462
  context: updated.context,
331
463
  reconsolidationCount: updated.reconsolidationCount,
332
464
  }),