prism-mcp-server 6.5.2 → 7.0.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.
@@ -1,52 +1,113 @@
1
+ /**
2
+ * Cognitive Memory Module — v7.0 (ACT-R Activation)
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════
5
+ * CHANGELOG:
6
+ * v5.2 — Ebbinghaus exponential decay: effective = base × 0.95^days
7
+ * v7.0 — REPLACED with ACT-R base-level activation model.
8
+ * The old formula conflated importance (a semantic property)
9
+ * with recency (a retrieval property). ACT-R separates them:
10
+ * - importance = semantic vote score (unchanged by time)
11
+ * - activation = B_i = ln(Σ t_j^(-d)) (retrieval signal)
12
+ *
13
+ * computeEffectiveImportance now uses ACT-R baseLevelActivation
14
+ * with a single-timestamp proxy (last_accessed_at or created_at).
15
+ * The full multi-timestamp pipeline runs in graphHandlers.ts
16
+ * where the access log is available.
17
+ *
18
+ * FILES THAT IMPORT THIS:
19
+ * - src/tools/graphHandlers.ts (knowledge search, session search)
20
+ * - src/tools/coreHandlers.ts (load context importance ordering)
21
+ * ═══════════════════════════════════════════════════════════════════
22
+ */
1
23
  import { getStorage } from "../storage/index.js";
2
24
  import { debugLog } from "./logger.js";
25
+ import { baseLevelActivation, parameterizedSigmoid, } from "./actrActivation.js";
26
+ import { PRISM_ACTR_ENABLED, PRISM_ACTR_DECAY, PRISM_ACTR_SIGMOID_MIDPOINT, PRISM_ACTR_SIGMOID_STEEPNESS, } from "../config.js";
3
27
  /**
4
- * Computes the effective importance of a memory using the Ebbinghaus Decay Formula.
5
- * Formula: Effective = Base * (0.95 ^ DaysSinceLastAccess)
28
+ * Computes the effective importance of a memory, combining the semantic
29
+ * importance score with a recency-based activation factor.
30
+ *
31
+ * v7.0 (ACT-R):
32
+ * effective = baseImportance × σ(B_i)
33
+ *
34
+ * Where B_i uses a single-timestamp proxy (last_accessed_at or created_at)
35
+ * and σ is the parameterized sigmoid from actrActivation.ts.
36
+ *
37
+ * This is a LIGHTWEIGHT estimator used by context loading and knowledge
38
+ * search to order results. The full multi-access-log pipeline runs in
39
+ * sessionSearchMemoryHandler (graphHandlers.ts).
40
+ *
41
+ * Fallback (PRISM_ACTR_ENABLED=false):
42
+ * Reverts to the v5.2 Ebbinghaus decay: base × 0.95^days
6
43
  *
7
44
  * @param baseImportance The raw importance score of the memory.
8
45
  * @param lastAccessedStr ISO string representing the last access time.
9
46
  * @param createdAtStr ISO string representing creation time (fallback).
10
- * @returns The decayed effective importance score, rounded to 2 decimal places.
47
+ * @returns The effective importance score, rounded to 2 decimal places.
11
48
  */
12
49
  export function computeEffectiveImportance(baseImportance, lastAccessedStr, createdAtStr) {
13
50
  if (baseImportance <= 0)
14
51
  return baseImportance;
15
- const now = Date.now();
52
+ const now = new Date();
16
53
  // Fallback to creation date if it has never been accessed
17
54
  const referenceDateStr = lastAccessedStr || createdAtStr;
18
- const referenceTime = new Date(referenceDateStr).getTime();
19
- // Calculate difference in days (preventing negative time decay if clocks skew)
20
- const diffMs = Math.max(0, now - referenceTime);
21
- const daysSinceAccess = diffMs / (1000 * 60 * 60 * 24);
22
- // Apply 5% decay per day
23
- const effective = baseImportance * Math.pow(0.95, daysSinceAccess);
24
- // Round to 2 decimal places for clean UI output
55
+ const referenceDate = new Date(referenceDateStr);
56
+ if (!PRISM_ACTR_ENABLED) {
57
+ // ── Legacy Ebbinghaus Decay (v5.2, deprecated) ──
58
+ const diffMs = Math.max(0, now.getTime() - referenceDate.getTime());
59
+ const daysSinceAccess = diffMs / (1000 * 60 * 60 * 24);
60
+ const effective = baseImportance * Math.pow(0.95, daysSinceAccess);
61
+ return Math.round(effective * 100) / 100;
62
+ }
63
+ // ── ACT-R Activation (v7.0) ──
64
+ // Use a single-timestamp proxy: treat last_accessed_at as one access event.
65
+ // This gives a simplified B_i = ln(t^(-d)) = -d × ln(t)
66
+ // The full multi-timestamp B_i runs in graphHandlers.ts search pipeline.
67
+ const Bi = baseLevelActivation([referenceDate], now, PRISM_ACTR_DECAY);
68
+ // Normalize to (0, 1) via parameterized sigmoid
69
+ const activationFactor = parameterizedSigmoid(Bi, PRISM_ACTR_SIGMOID_MIDPOINT, PRISM_ACTR_SIGMOID_STEEPNESS);
70
+ // Scale importance by activation factor
71
+ // Fresh memory: factor ≈ 0.99 → nearly full importance
72
+ // Cold memory: factor ≈ 0.05 → heavily discounted
73
+ const effective = baseImportance * activationFactor;
25
74
  return Math.round(effective * 100) / 100;
26
75
  }
27
76
  /**
28
- * Fire-and-forget helper to update the last_accessed_at timestamp for retrieved memories.
77
+ * Fire-and-forget helper to record access events for retrieved memories.
78
+ *
79
+ * v7.0: Uses the storage.logAccess() method which delegates to the
80
+ * AccessLogBuffer for batched, contention-free writes.
81
+ * Also updates legacy last_accessed_at for backward compat.
29
82
  *
30
83
  * @param ids Array of memory IDs that were just accessed
84
+ * @param contextHash Optional search query fingerprint
31
85
  */
32
- export function updateLastAccessed(ids) {
86
+ export function recordMemoryAccess(ids, contextHash) {
33
87
  if (!ids || ids.length === 0)
34
88
  return;
35
- const now = new Date().toISOString();
36
89
  // Fire and forget, don't block execution
37
90
  getStorage().then(storage => {
38
- if (typeof storage.updateLastAccessed === 'function') {
39
- storage.updateLastAccessed(ids).catch(error => {
40
- debugLog(`[CognitiveMemory] Failed to update last_accessed_at: ${error instanceof Error ? error.message : String(error)}`);
41
- });
42
- }
43
- else {
44
- // Fallback for older interface implementation
45
- Promise.allSettled(ids.map(id => storage.patchLedger(id, { last_accessed_at: now }))).then(() => {
46
- debugLog(`[CognitiveMemory] Updated last_accessed_at for ${ids.length} memories.`);
47
- });
91
+ // v7.0: Log access events to memory_access_log via buffer
92
+ for (const id of ids) {
93
+ storage.logAccess(id, contextHash);
48
94
  }
95
+ // Backward compat: batch-update legacy last_accessed_at via IN clause.
96
+ // Uses the optimized updateLastAccessed (single SQL with WHERE id IN (...))
97
+ // instead of N individual patchLedger calls.
98
+ storage.updateLastAccessed(ids).then(() => {
99
+ debugLog(`[CognitiveMemory] Recorded ${ids.length} memory accesses.`);
100
+ }).catch(() => {
101
+ // Non-fatal: legacy timestamp is best-effort
102
+ });
49
103
  }).catch(error => {
50
- debugLog(`[CognitiveMemory] Failed to get storage backend for last_accessed_at update: ${error instanceof Error ? error.message : String(error)}`);
104
+ debugLog(`[CognitiveMemory] Failed to record memory access: ${error instanceof Error ? error.message : String(error)}`);
51
105
  });
52
106
  }
107
+ /**
108
+ * @deprecated Use recordMemoryAccess() instead. Kept for backward compat.
109
+ * Fire-and-forget helper to update the last_accessed_at timestamp.
110
+ */
111
+ export function updateLastAccessed(ids) {
112
+ recordMemoryAccess(ids);
113
+ }
@@ -103,7 +103,7 @@
103
103
  * @returns A complete MemoryTrace object ready for serialization
104
104
  */
105
105
  export function createMemoryTrace(params) {
106
- return {
106
+ const trace = {
107
107
  strategy: params.strategy,
108
108
  query: params.query,
109
109
  result_count: params.resultCount,
@@ -117,6 +117,31 @@ export function createMemoryTrace(params) {
117
117
  timestamp: new Date().toISOString(),
118
118
  project: params.project,
119
119
  };
120
+ // v7.0: Attach ACT-R metrics only when present (keeps trace clean when disabled)
121
+ if (params.actrEnabled !== undefined) {
122
+ trace.actr_enabled = params.actrEnabled;
123
+ if (params.actrBaseLevelMean !== undefined) {
124
+ const v = Math.round(params.actrBaseLevelMean * 1000) / 1000;
125
+ trace.actr_base_level_mean = v;
126
+ trace.actr_base_level_activation = v; // plan-documented alias
127
+ }
128
+ if (params.actrSpreadingMean !== undefined) {
129
+ const v = Math.round(params.actrSpreadingMean * 1000) / 1000;
130
+ trace.actr_spreading_mean = v;
131
+ trace.actr_spreading_activation = v; // plan-documented alias
132
+ }
133
+ if (params.actrSigmoidMean !== undefined) {
134
+ const v = Math.round(params.actrSigmoidMean * 1000) / 1000;
135
+ trace.actr_sigmoid_mean = v;
136
+ trace.actr_sigmoid_activation = v; // plan-documented alias
137
+ }
138
+ if (params.actrCompositeMean !== undefined) {
139
+ const v = Math.round(params.actrCompositeMean * 1000) / 1000;
140
+ trace.actr_composite_mean = v;
141
+ trace.actr_composite_score = v; // plan-documented alias
142
+ }
143
+ }
144
+ return trace;
120
145
  }
121
146
  /**
122
147
  * Format a MemoryTrace into an MCP content block.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "6.5.2",
3
+ "version": "7.0.0",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents — persistent memory (SQLite/Supabase), behavioral learning & IDE rules sync, multimodal VLM image captioning, pluggable LLM providers (OpenAI/Anthropic/Gemini/Ollama), OpenTelemetry distributed tracing, GDPR export, multi-agent Hivemind sync, time travel, visual Mind Palace dashboard. Zero-config local mode.",
6
6
  "module": "index.ts",
@@ -65,7 +65,10 @@
65
65
  "persistent-memory",
66
66
  "zero-config",
67
67
  "auto-capture",
68
- "dashboard"
68
+ "dashboard",
69
+ "actr",
70
+ "cognitive-memory",
71
+ "activation-memory"
69
72
  ],
70
73
  "homepage": "https://github.com/dcostenco/prism-mcp",
71
74
  "repository": {