@cogmem/engram 0.3.0 → 0.3.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.
@@ -1,8 +1,5 @@
1
1
  import type { CognitiveConfig } from "../config/defaults.ts";
2
2
  import type { EngramStorage } from "../storage/sqlite.ts";
3
- import type { Memory, ConsolidationLog } from "./memory.ts";
4
- import { generateId } from "./memory.ts";
5
- import { refreshActivations } from "./forgetting.ts";
6
3
  import {
7
4
  formSemanticAssociations,
8
5
  formTemporalAssociations,
@@ -10,6 +7,9 @@ import {
10
7
  formCausalAssociations,
11
8
  } from "./associations.ts";
12
9
  import { encode } from "./encoder.ts";
10
+ import { refreshActivations } from "./forgetting.ts";
11
+ import type { Memory, ConsolidationLog } from "./memory.ts";
12
+ import { generateId } from "./memory.ts";
13
13
  import { extractKeywords, tokenize } from "./search.ts";
14
14
 
15
15
  export interface ConsolidationResult {
@@ -69,11 +69,26 @@ export function consolidate(
69
69
  const remainingMemories = storage.getAllMemories();
70
70
  for (const memory of remainingMemories) {
71
71
  const temporalAssocs = formTemporalAssociations(storage, memory, config, currentTime);
72
- const semanticAssocs = formSemanticAssociations(storage, memory, currentTime, remainingMemories);
73
- const emotionalAssocs = formEmotionalAssociations(storage, memory, currentTime, remainingMemories);
72
+ const semanticAssocs = formSemanticAssociations(
73
+ storage,
74
+ memory,
75
+ currentTime,
76
+ remainingMemories,
77
+ );
78
+ const emotionalAssocs = formEmotionalAssociations(
79
+ storage,
80
+ memory,
81
+ currentTime,
82
+ remainingMemories,
83
+ );
74
84
  const causalAssocs = formCausalAssociations(storage, memory, config, currentTime);
75
85
 
76
- for (const assoc of [...temporalAssocs, ...semanticAssocs, ...emotionalAssocs, ...causalAssocs]) {
86
+ for (const assoc of [
87
+ ...temporalAssocs,
88
+ ...semanticAssocs,
89
+ ...emotionalAssocs,
90
+ ...causalAssocs,
91
+ ]) {
77
92
  result.associationsDiscovered++;
78
93
  result.discoveredAssociationPairs.push([assoc.sourceId, assoc.targetId]);
79
94
  }
@@ -1,10 +1,10 @@
1
1
  import type { CognitiveConfig } from "../config/defaults.ts";
2
2
  import type { EngramStorage } from "../storage/sqlite.ts";
3
- import type { Memory, EncodeInput } from "./memory.ts";
4
- import { generateMemoryId } from "./memory.ts";
5
- import { defaultEmotionWeight } from "./emotional-tag.ts";
6
3
  import { baseLevelActivation } from "./activation.ts";
7
4
  import { formEmotionalAssociations, formCausalAssociations } from "./associations.ts";
5
+ import { defaultEmotionWeight } from "./emotional-tag.ts";
6
+ import type { Memory, EncodeInput } from "./memory.ts";
7
+ import { generateMemoryId } from "./memory.ts";
8
8
 
9
9
  export function encode(
10
10
  storage: EngramStorage,
@@ -1,8 +1,9 @@
1
+ import { execSync } from "node:child_process";
2
+ import { basename } from "node:path";
3
+
1
4
  import type { CognitiveConfig } from "../config/defaults.ts";
2
5
  import { loadConfig } from "../config/defaults.ts";
3
6
  import { EngramStorage } from "../storage/sqlite.ts";
4
- import { execSync } from "node:child_process";
5
- import { basename } from "node:path";
6
7
 
7
8
  export function detectProjectContext(): string | null {
8
9
  try {
@@ -3,7 +3,10 @@ import type { EngramStorage } from "../storage/sqlite.ts";
3
3
  import { baseLevelActivation } from "./activation.ts";
4
4
 
5
5
  // R(t) = e^{-t/S}
6
- export function ebbinghausRetention(timeSinceLastRecall: number, strength: number): number {
6
+ export function ebbinghausRetention(
7
+ timeSinceLastRecall: number,
8
+ strength: number
9
+ ): number {
7
10
  if (strength <= 0) return 0;
8
11
  return Math.exp(-timeSinceLastRecall / strength);
9
12
  }
@@ -12,7 +15,7 @@ export function memoryStrength(
12
15
  recallCount: number,
13
16
  emotionWeight: number,
14
17
  associationCount: number,
15
- emotionalBoostFactor: number,
18
+ emotionalBoostFactor: number
16
19
  ): number {
17
20
  const recallStrength = 1 + recallCount * 0.8;
18
21
  const emotionalStrength = 1 + emotionWeight * emotionalBoostFactor;
@@ -23,7 +26,7 @@ export function memoryStrength(
23
26
  export function refreshActivations(
24
27
  storage: EngramStorage,
25
28
  config: CognitiveConfig,
26
- now?: number,
29
+ now?: number
27
30
  ): { updated: number; atRisk: number } {
28
31
  const currentTime = now ?? Date.now();
29
32
  const memories = storage.getAllMemories();
@@ -34,7 +37,11 @@ export function refreshActivations(
34
37
  if (memory.type === "procedural") continue;
35
38
 
36
39
  const timestamps = storage.getAccessTimestamps(memory.id);
37
- const newActivation = baseLevelActivation(timestamps, currentTime, config.decayRate);
40
+ const newActivation = baseLevelActivation(
41
+ timestamps,
42
+ currentTime,
43
+ config.decayRate
44
+ );
38
45
 
39
46
  const emotionBoost =
40
47
  memory.emotionWeight > 0
@@ -32,7 +32,8 @@ export const AssociationType = {
32
32
  Emotional: "emotional",
33
33
  Causal: "causal",
34
34
  } as const;
35
- export type AssociationType = (typeof AssociationType)[keyof typeof AssociationType];
35
+ export type AssociationType =
36
+ (typeof AssociationType)[keyof typeof AssociationType];
36
37
 
37
38
  export const AccessType = {
38
39
  Encode: "encode",
@@ -1,7 +1,7 @@
1
1
  import type { CognitiveConfig } from "../config/defaults.ts";
2
2
  import type { EngramStorage } from "../storage/sqlite.ts";
3
- import type { Memory } from "./memory.ts";
4
3
  import { encode } from "./encoder.ts";
4
+ import type { Memory } from "./memory.ts";
5
5
 
6
6
  export function encodeProcedural(
7
7
  storage: EngramStorage,
@@ -1,8 +1,11 @@
1
1
  import type { CognitiveConfig } from "../config/defaults.ts";
2
2
  import type { EngramStorage } from "../storage/sqlite.ts";
3
- import type { Memory, RecallResult } from "./memory.ts";
4
- import { computeActivation, spreadingActivationStrength } from "./activation.ts";
3
+ import {
4
+ computeActivation,
5
+ spreadingActivationStrength,
6
+ } from "./activation.ts";
5
7
  import { getSpreadingActivationTargets } from "./associations.ts";
8
+ import type { Memory, RecallResult } from "./memory.ts";
6
9
  import { getWorkingMemoryIds } from "./working-memory.ts";
7
10
 
8
11
  export function recall(
@@ -16,7 +19,7 @@ export function recall(
16
19
  context?: string;
17
20
  now?: number;
18
21
  deterministic?: boolean;
19
- },
22
+ }
20
23
  ): RecallResult[] {
21
24
  const now = options?.now ?? Date.now();
22
25
  const limit = options?.limit ?? 10;
@@ -31,14 +34,18 @@ export function recall(
31
34
  for (const id of ftsIds) seedIds.add(id);
32
35
 
33
36
  if (options?.context) {
34
- const contextMatches = storage.getMemoriesByContext(options.context, options?.type, limit * 2);
37
+ const contextMatches = storage.getMemoriesByContext(
38
+ options.context,
39
+ options?.type,
40
+ limit * 2
41
+ );
35
42
  for (const m of contextMatches) seedIds.add(m.id);
36
43
  }
37
44
 
38
45
  const topByActivation = storage.getTopMemoriesByActivation(
39
46
  limit,
40
47
  options?.type,
41
- options?.context,
48
+ options?.context
42
49
  );
43
50
  for (const m of topByActivation) {
44
51
  seedIds.add(m.id);
@@ -56,7 +63,10 @@ export function recall(
56
63
  for (const t of targets) {
57
64
  candidateIds.add(t.memoryId);
58
65
  const existing = graphBoosts.get(t.memoryId) ?? 0;
59
- graphBoosts.set(t.memoryId, existing + t.activationBoost * primingWeight);
66
+ graphBoosts.set(
67
+ t.memoryId,
68
+ existing + t.activationBoost * primingWeight
69
+ );
60
70
  }
61
71
  }
62
72
  }
@@ -66,7 +76,11 @@ export function recall(
66
76
  const m = storage.getMemory(id);
67
77
  if (!m) continue;
68
78
  if (options?.type && m.type !== options.type) continue;
69
- if (options?.context && (!m.context || !m.context.startsWith(options.context))) continue;
79
+ if (
80
+ options?.context &&
81
+ (!m.context || !m.context.startsWith(options.context))
82
+ )
83
+ continue;
70
84
  candidateMap.set(m.id, m);
71
85
  }
72
86
 
@@ -84,9 +98,13 @@ export function recall(
84
98
  const allAssocs = [...assocFrom, ...assocTo];
85
99
 
86
100
  for (const assoc of allAssocs) {
87
- const otherId = assoc.sourceId === memory.id ? assoc.targetId : assoc.sourceId;
101
+ const otherId =
102
+ assoc.sourceId === memory.id ? assoc.targetId : assoc.sourceId;
88
103
  const fanCount = storage.getFanCount(otherId);
89
- const strength = spreadingActivationStrength(config.maxSpreadingActivation, fanCount);
104
+ const strength = spreadingActivationStrength(
105
+ config.maxSpreadingActivation,
106
+ fanCount
107
+ );
90
108
  spreadingSum += assoc.strength * strength;
91
109
  }
92
110
  }
@@ -12,7 +12,7 @@ export function reconsolidate(
12
12
  storage: EngramStorage,
13
13
  memory: Memory,
14
14
  recallContext: ReconsolidationContext,
15
- config: CognitiveConfig,
15
+ config: CognitiveConfig
16
16
  ): Memory {
17
17
  const blendRate = config.reconsolidationBlendRate;
18
18
 
@@ -24,7 +24,10 @@ export function reconsolidate(
24
24
  memory.context = recallContext.newContext;
25
25
  }
26
26
 
27
- if (recallContext.currentEmotion && recallContext.currentEmotion !== memory.emotion) {
27
+ if (
28
+ recallContext.currentEmotion &&
29
+ recallContext.currentEmotion !== memory.emotion
30
+ ) {
28
31
  const blendedWeight =
29
32
  memory.emotionWeight * (1 - blendRate) +
30
33
  (recallContext.currentEmotionWeight ?? 0.5) * blendRate;
@@ -6,7 +6,7 @@ export function pushFocus(
6
6
  storage: EngramStorage,
7
7
  content: string,
8
8
  config: CognitiveConfig,
9
- options?: { memoryRef?: string; now?: number },
9
+ options?: { memoryRef?: string; now?: number }
10
10
  ): { slot: WorkingMemorySlot; evicted: WorkingMemorySlot | null } {
11
11
  const now = options?.now ?? Date.now();
12
12
  const currentSlots = storage.getWorkingMemory();
@@ -14,7 +14,9 @@ export function pushFocus(
14
14
  let evicted: WorkingMemorySlot | null = null;
15
15
 
16
16
  if (currentSlots.length >= config.workingMemoryCapacity) {
17
- const oldest = currentSlots.reduce((min, s) => (s.pushedAt < min.pushedAt ? s : min));
17
+ const oldest = currentSlots.reduce((min, s) =>
18
+ s.pushedAt < min.pushedAt ? s : min
19
+ );
18
20
  evicted = oldest;
19
21
  storage.removeWorkingMemorySlot(oldest.slot);
20
22
  }
@@ -38,7 +40,9 @@ export function popFocus(storage: EngramStorage): WorkingMemorySlot | null {
38
40
  const slots = storage.getWorkingMemory();
39
41
  if (slots.length === 0) return null;
40
42
 
41
- const newest = slots.reduce((max, s) => (s.pushedAt > max.pushedAt ? s : max));
43
+ const newest = slots.reduce((max, s) =>
44
+ s.pushedAt > max.pushedAt ? s : max
45
+ );
42
46
 
43
47
  storage.removeWorkingMemorySlot(newest.slot);
44
48
  return newest;
@@ -63,7 +67,7 @@ export function getWorkingMemoryIds(storage: EngramStorage): string[] {
63
67
 
64
68
  export function focusUtilization(
65
69
  storage: EngramStorage,
66
- config: CognitiveConfig,
70
+ config: CognitiveConfig
67
71
  ): { used: number; capacity: number; utilization: number } {
68
72
  const used = storage.getWorkingMemoryCount();
69
73
  return {
package/src/mcp/server.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod/v4";
5
+
5
6
  import { EngramEngine } from "../core/engine.ts";
6
7
  import { MemoryType, Emotion } from "../core/memory.ts";
7
8
  import { handleStore, handleRecall, handleManage } from "./tools.ts";
@@ -22,10 +23,21 @@ server.registerTool(
22
23
  z.object({
23
24
  action: z.literal("encode"),
24
25
  content: z.string().describe("Memory content"),
25
- type: z.nativeEnum(MemoryType).optional().describe("Memory type (default: semantic)"),
26
+ type: z
27
+ .nativeEnum(MemoryType)
28
+ .optional()
29
+ .describe("Memory type (default: semantic)"),
26
30
  emotion: z.nativeEnum(Emotion).optional().describe("Emotional tag"),
27
- emotionWeight: z.number().min(0).max(1).optional().describe("Emotion intensity 0-1"),
28
- context: z.string().optional().describe("Context tag (e.g. project:acme)"),
31
+ emotionWeight: z
32
+ .number()
33
+ .min(0)
34
+ .max(1)
35
+ .optional()
36
+ .describe("Emotion intensity 0-1"),
37
+ context: z
38
+ .string()
39
+ .optional()
40
+ .describe("Context tag (e.g. project:acme)"),
29
41
  }),
30
42
  z.object({
31
43
  action: z.literal("encode_batch"),
@@ -37,7 +49,7 @@ server.registerTool(
37
49
  emotion: z.nativeEnum(Emotion).optional(),
38
50
  emotionWeight: z.number().min(0).max(1).optional(),
39
51
  context: z.string().optional(),
40
- }),
52
+ })
41
53
  )
42
54
  .min(1)
43
55
  .max(50)
@@ -47,12 +59,20 @@ server.registerTool(
47
59
  action: z.literal("reconsolidate"),
48
60
  id: z.string().describe("Memory ID to update"),
49
61
  newContext: z.string().optional().describe("New context to blend"),
50
- currentEmotion: z.nativeEnum(Emotion).optional().describe("Current emotional state"),
51
- currentEmotionWeight: z.number().min(0).max(1).optional().describe("Emotion intensity"),
62
+ currentEmotion: z
63
+ .nativeEnum(Emotion)
64
+ .optional()
65
+ .describe("Current emotional state"),
66
+ currentEmotionWeight: z
67
+ .number()
68
+ .min(0)
69
+ .max(1)
70
+ .optional()
71
+ .describe("Emotion intensity"),
52
72
  }),
53
73
  ]),
54
74
  },
55
- async (args) => handleStore(engine, args),
75
+ async (args) => handleStore(engine, args)
56
76
  );
57
77
 
58
78
  server.registerTool(
@@ -67,9 +87,15 @@ server.registerTool(
67
87
  limit: z.number().optional().describe("Max results (default: 5)"),
68
88
  type: z.nativeEnum(MemoryType).optional().describe("Filter by type"),
69
89
  context: z.string().optional().describe("Filter by context"),
70
- associative: z.boolean().optional().describe("Spreading activation (default: true)"),
90
+ associative: z
91
+ .boolean()
92
+ .optional()
93
+ .describe("Spreading activation (default: true)"),
71
94
  verbose: z.boolean().optional().describe("Full fields"),
72
- format: z.enum(["full", "content", "ids"]).optional().describe("Response format (default: full)"),
95
+ format: z
96
+ .enum(["full", "content", "ids"])
97
+ .optional()
98
+ .describe("Response format (default: full)"),
73
99
  }),
74
100
  z.object({
75
101
  action: z.literal("inspect"),
@@ -80,15 +106,21 @@ server.registerTool(
80
106
  type: z.nativeEnum(MemoryType).optional().describe("Filter by type"),
81
107
  context: z.string().optional().describe("Filter by context prefix"),
82
108
  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)"),
109
+ offset: z
110
+ .number()
111
+ .optional()
112
+ .describe("Skip first N results (default: 0)"),
113
+ format: z
114
+ .enum(["full", "content", "ids"])
115
+ .optional()
116
+ .describe("Response format (default: full)"),
85
117
  }),
86
118
  z.object({
87
119
  action: z.literal("stats"),
88
120
  }),
89
121
  ]),
90
122
  },
91
- async (args) => handleRecall(engine, args),
123
+ async (args) => handleRecall(engine, args)
92
124
  );
93
125
 
94
126
  server.registerTool(
@@ -103,7 +135,10 @@ server.registerTool(
103
135
  z.object({
104
136
  action: z.literal("focus_push"),
105
137
  content: z.string().describe("Content to hold in focus"),
106
- memoryRef: z.string().optional().describe("Reference to existing memory ID"),
138
+ memoryRef: z
139
+ .string()
140
+ .optional()
141
+ .describe("Reference to existing memory ID"),
107
142
  }),
108
143
  z.object({
109
144
  action: z.literal("focus_pop"),
@@ -117,13 +152,16 @@ server.registerTool(
117
152
  z.object({
118
153
  action: z.literal("recall_to_focus"),
119
154
  cue: z.string().describe("Recall cue"),
120
- limit: z.number().optional().describe("Max memories to load (default: 3)"),
155
+ limit: z
156
+ .number()
157
+ .optional()
158
+ .describe("Max memories to load (default: 3)"),
121
159
  type: z.nativeEnum(MemoryType).optional().describe("Filter by type"),
122
160
  context: z.string().optional().describe("Filter by context"),
123
161
  }),
124
162
  ]),
125
163
  },
126
- async (args) => handleManage(engine, args),
164
+ async (args) => handleManage(engine, args)
127
165
  );
128
166
 
129
167
  async function main() {