cozo-memory 1.2.0 → 1.2.2

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/dist/index.js CHANGED
@@ -18,6 +18,12 @@ const hybrid_search_1 = require("./hybrid-search");
18
18
  const inference_engine_1 = require("./inference-engine");
19
19
  const dynamic_fusion_1 = require("./dynamic-fusion");
20
20
  const adaptive_retrieval_1 = require("./adaptive-retrieval");
21
+ const zettelkasten_evolution_1 = require("./zettelkasten-evolution");
22
+ const memory_activation_1 = require("./memory-activation");
23
+ const emotional_salience_1 = require("./emotional-salience");
24
+ const proactive_suggestions_1 = require("./proactive-suggestions");
25
+ const spreading_activation_1 = require("./spreading-activation");
26
+ const temporal_conflict_resolution_1 = require("./temporal-conflict-resolution");
21
27
  exports.DB_PATH = path_1.default.resolve(__dirname, "..", "memory_db.cozo");
22
28
  const DB_ENGINE = process.env.DB_ENGINE || "sqlite"; // "sqlite" or "rocksdb"
23
29
  exports.USER_ENTITY_ID = "global_user_profile";
@@ -33,6 +39,15 @@ class MemoryServer {
33
39
  inferenceEngine;
34
40
  initPromise;
35
41
  compactionLocks = new Set();
42
+ zettelkastenService = null;
43
+ activationService = null;
44
+ salienceService = null;
45
+ suggestionsService = null;
46
+ spreadingService = null;
47
+ conflictService = null;
48
+ logicalEdgesService = null;
49
+ hierarchicalMemoryService = null;
50
+ queryAwareTraversal = null;
36
51
  // Metrics tracking
37
52
  metrics = {
38
53
  operations: {
@@ -93,6 +108,143 @@ class MemoryServer {
93
108
  })();
94
109
  this.registerTools();
95
110
  }
111
+ getZettelkastenService() {
112
+ if (!this.zettelkastenService) {
113
+ this.zettelkastenService = new zettelkasten_evolution_1.ZettelkastenEvolutionService(this.db, this.embeddingService, {
114
+ enableEvolution: true,
115
+ similarityThreshold: 0.7,
116
+ maxRelatedNotes: 5,
117
+ minKeywordFrequency: 2,
118
+ autoExtractKeywords: true,
119
+ autoBidirectionalLinks: true,
120
+ enrichmentDepth: 'shallow'
121
+ });
122
+ }
123
+ return this.zettelkastenService;
124
+ }
125
+ getActivationService() {
126
+ if (!this.activationService) {
127
+ this.activationService = new memory_activation_1.MemoryActivationService(this.db, {
128
+ initialStrength: 1.0,
129
+ strengthIncrement: 1.0,
130
+ maxStrength: 20.0,
131
+ retentionThreshold: 0.15,
132
+ decayBase: Math.E,
133
+ timeUnit: 'days'
134
+ });
135
+ }
136
+ return this.activationService;
137
+ }
138
+ getSalienceService() {
139
+ if (!this.salienceService) {
140
+ this.salienceService = new emotional_salience_1.EmotionalSalienceService(this.db, {
141
+ enableSalience: true,
142
+ salienceBoostFactor: 2.0,
143
+ decaySlowdownFactor: 0.5,
144
+ minSalienceThreshold: 0.3
145
+ });
146
+ }
147
+ return this.salienceService;
148
+ }
149
+ getSuggestionsService() {
150
+ if (!this.suggestionsService) {
151
+ // Create a minimal DatabaseService wrapper for ProactiveSuggestionsService
152
+ const dbWrapper = {
153
+ getEntity: async (id) => {
154
+ const res = await this.db.run('?[name, type, embedding] := *entity{id: $id, name, type, embedding, @ "NOW"}', { id });
155
+ if (res.rows.length === 0)
156
+ return null;
157
+ return {
158
+ id,
159
+ name: res.rows[0][0],
160
+ type: res.rows[0][1],
161
+ embedding: res.rows[0][2],
162
+ };
163
+ },
164
+ getRelations: async (fromId, toId) => {
165
+ let query = '?[from_id, to_id, relation_type, strength] := *relationship{from_id, to_id, relation_type, strength, @ "NOW"}';
166
+ const params = {};
167
+ if (fromId) {
168
+ query += ', from_id = $from_id';
169
+ params.from_id = fromId;
170
+ }
171
+ if (toId) {
172
+ query += ', to_id = $to_id';
173
+ params.to_id = toId;
174
+ }
175
+ const res = await this.db.run(query, params);
176
+ return res.rows.map((r) => ({
177
+ from_id: r[0],
178
+ to_id: r[1],
179
+ relation_type: r[2],
180
+ strength: r[3],
181
+ }));
182
+ },
183
+ vectorSearchEntity: async (embedding, limit) => {
184
+ const res = await this.db.run(`?[id, name, type, dist] := ~entity:embedding_index{embedding: $embedding | query: $limit, ef: 100, bind_distance: dist}, *entity{id, name, type, @ "NOW"}`, { embedding, limit });
185
+ return res.rows;
186
+ },
187
+ };
188
+ this.suggestionsService = new proactive_suggestions_1.ProactiveSuggestionsService(dbWrapper, this.embeddingService, {
189
+ maxSuggestions: 10,
190
+ minConfidence: 0.5,
191
+ enableVectorSimilarity: true,
192
+ enableCommonNeighbors: true,
193
+ enableInference: true,
194
+ enableGraphProximity: true
195
+ });
196
+ }
197
+ return this.suggestionsService;
198
+ }
199
+ getSpreadingService() {
200
+ if (!this.spreadingService) {
201
+ this.spreadingService = new spreading_activation_1.SpreadingActivationService(this.db, this.embeddingService, {
202
+ spreadingFactor: 0.8,
203
+ decayFactor: 0.5,
204
+ temporalDecay: 0.01,
205
+ inhibitionBeta: 0.15,
206
+ inhibitionTopM: 7,
207
+ propagationSteps: 3,
208
+ activationThreshold: 0.01,
209
+ sigmoidGamma: 5.0,
210
+ sigmoidTheta: 0.5
211
+ });
212
+ }
213
+ return this.spreadingService;
214
+ }
215
+ getConflictService() {
216
+ if (!this.conflictService) {
217
+ this.conflictService = new temporal_conflict_resolution_1.TemporalConflictResolutionService(this.db, this.embeddingService, {
218
+ similarityThreshold: 0.85,
219
+ contradictionThreshold: 0.3,
220
+ timeWindowDays: 365,
221
+ autoResolve: false,
222
+ preserveAuditTrail: true
223
+ });
224
+ }
225
+ return this.conflictService;
226
+ }
227
+ getLogicalEdgesService() {
228
+ if (!this.logicalEdgesService) {
229
+ const { LogicalEdgesService } = require('./logical-edges-service');
230
+ this.logicalEdgesService = new LogicalEdgesService(this.db);
231
+ }
232
+ return this.logicalEdgesService;
233
+ }
234
+ getHierarchicalMemoryService() {
235
+ if (!this.hierarchicalMemoryService) {
236
+ const { HierarchicalMemoryService } = require('./hierarchical-memory');
237
+ this.hierarchicalMemoryService = new HierarchicalMemoryService(this.db, this.embeddingService);
238
+ }
239
+ return this.hierarchicalMemoryService;
240
+ }
241
+ getQueryAwareTraversal() {
242
+ if (!this.queryAwareTraversal) {
243
+ const { QueryAwareTraversal } = require('./query-aware-traversal');
244
+ this.queryAwareTraversal = new QueryAwareTraversal(this.db, this.embeddingService);
245
+ }
246
+ return this.queryAwareTraversal;
247
+ }
96
248
  async janitorCleanup(args) {
97
249
  await this.initPromise;
98
250
  console.error(`[Janitor] Starting cleanup (auto-compressing sessions first)...`);
@@ -3213,10 +3365,32 @@ Format MUST start with "ExecutiveSummary: " followed by the consolidated content
3213
3365
  action: zod_1.z.literal("stop_task"),
3214
3366
  id: zod_1.z.string().describe("ID of the task to stop"),
3215
3367
  }).passthrough(),
3368
+ zod_1.z.object({
3369
+ action: zod_1.z.literal("enrich_observation"),
3370
+ observation_id: zod_1.z.string().describe("ID of the observation to enrich with Zettelkasten metadata"),
3371
+ }).passthrough(),
3372
+ zod_1.z.object({
3373
+ action: zod_1.z.literal("record_memory_access"),
3374
+ observation_id: zod_1.z.string().describe("ID of the observation to record access for (ACT-R)"),
3375
+ }).passthrough(),
3376
+ zod_1.z.object({
3377
+ action: zod_1.z.literal("prune_weak_memories"),
3378
+ dry_run: zod_1.z.boolean().optional().default(true).describe("If true, only shows candidates without deleting"),
3379
+ entity_id: zod_1.z.string().optional().describe("Optional: Only prune memories for specific entity"),
3380
+ }).passthrough(),
3381
+ zod_1.z.object({
3382
+ action: zod_1.z.literal("detect_conflicts"),
3383
+ entity_id: zod_1.z.string().describe("Entity ID to detect conflicts for"),
3384
+ }),
3385
+ zod_1.z.object({
3386
+ action: zod_1.z.literal("resolve_conflicts"),
3387
+ entity_id: zod_1.z.string().describe("Entity ID to resolve conflicts for"),
3388
+ auto_resolve: zod_1.z.boolean().optional().default(false).describe("Automatically resolve all conflicts"),
3389
+ }),
3216
3390
  ]);
3217
3391
  const MutateMemoryParameters = zod_1.z.object({
3218
3392
  action: zod_1.z
3219
- .enum(["create_entity", "update_entity", "delete_entity", "add_observation", "create_relation", "run_transaction", "add_inference_rule", "ingest_file", "start_session", "stop_session", "start_task", "stop_task", "invalidate_observation", "invalidate_relation"])
3393
+ .enum(["create_entity", "update_entity", "delete_entity", "add_observation", "create_relation", "run_transaction", "add_inference_rule", "ingest_file", "start_session", "stop_session", "start_task", "stop_task", "invalidate_observation", "invalidate_relation", "enrich_observation", "record_memory_access", "prune_weak_memories", "detect_conflicts", "resolve_conflicts"])
3220
3394
  .describe("Action (determines which fields are required)"),
3221
3395
  name: zod_1.z.string().optional().describe("For create_entity (required) or add_inference_rule (required)"),
3222
3396
  type: zod_1.z.string().optional().describe("For create_entity (required)"),
@@ -3238,7 +3412,8 @@ Format MUST start with "ExecutiveSummary: " followed by the consolidated content
3238
3412
  relation_type: zod_1.z.string().optional().describe("For create_relation (required)"),
3239
3413
  strength: zod_1.z.number().min(0).max(1).optional().describe("Optional for create_relation"),
3240
3414
  metadata: MetadataSchema.optional().describe("Optional for create_entity/update_entity/add_observation/create_relation/ingest_file"),
3241
- observation_id: zod_1.z.string().optional().describe("For invalidate_observation (required)"),
3415
+ observation_id: zod_1.z.string().optional().describe("For invalidate_observation (required) or enrich_observation (required) or record_memory_access (required)"),
3416
+ dry_run: zod_1.z.boolean().optional().describe("For prune_weak_memories: if true, only shows candidates"),
3242
3417
  operations: zod_1.z.array(zod_1.z.object({
3243
3418
  action: zod_1.z.enum(["create_entity", "add_observation", "create_relation", "delete_entity"]),
3244
3419
  params: zod_1.z.any().describe("Parameters for the operation as an object")
@@ -3271,6 +3446,11 @@ Supported actions:
3271
3446
  - 'stop_task': Marks a task as completed. Params: { id: string }.
3272
3447
  - 'invalidate_observation': Invalidates (soft-deletes) an observation at the current time. Params: { observation_id: string }.
3273
3448
  - 'invalidate_relation': Invalidates (soft-deletes) a relationship at the current time. Params: { from_id: string, to_id: string, relation_type: string }.
3449
+ - 'enrich_observation': Manually trigger Zettelkasten enrichment for an observation. Params: { observation_id: string }. Extracts keywords/tags, finds related observations, creates bidirectional links, and updates metadata.
3450
+ - 'record_memory_access': Record access to an observation for ACT-R memory activation tracking. Params: { observation_id: string }. Updates access_count and last_access_time metadata.
3451
+ - 'prune_weak_memories': Delete observations with activation below retention threshold. Params: { dry_run?: boolean (default: true), entity_id?: string }. Returns: { pruned: number, preserved: number, candidates: ActivationScore[] }.
3452
+ - 'detect_conflicts': Detect temporal conflicts for an entity. Params: { entity_id: string }. Returns: { conflicts: [{ older_observation_id, newer_observation_id, conflict_type, confidence, reason }], count }. Detects redundancy, contradictions, and superseded facts.
3453
+ - 'resolve_conflicts': Resolve temporal conflicts by invalidating older observations. Params: { entity_id: string, auto_resolve?: boolean }. Returns: { resolved_conflicts, invalidated_observations, audit_observations }. Creates audit trail if enabled.
3274
3454
 
3275
3455
  Validation: Invalid syntax or missing columns in inference rules will result in errors.`,
3276
3456
  parameters: MutateMemoryParameters,
@@ -3316,6 +3496,117 @@ Validation: Invalid syntax or missing columns in inference rules will result in
3316
3496
  return JSON.stringify(await this.startTask(rest));
3317
3497
  if (action === "stop_task")
3318
3498
  return JSON.stringify(await this.stopTask(rest));
3499
+ if (action === "enrich_observation") {
3500
+ try {
3501
+ console.log('[mutate_memory] Enriching observation:', rest.observation_id);
3502
+ // Get observation details
3503
+ const obsRes = await this.db.run(`?[obs_id, entity_id, text, embedding] := *observation{id: $id, entity_id, text, embedding, @ "NOW"}, obs_id = $id`, { id: rest.observation_id });
3504
+ if (obsRes.rows.length === 0) {
3505
+ return JSON.stringify({ error: "Observation not found" });
3506
+ }
3507
+ const [obs_id, entity_id, text, embedding] = obsRes.rows[0];
3508
+ // Enrich the observation
3509
+ const result = await this.getZettelkastenService().enrichObservation(obs_id, text, embedding, entity_id);
3510
+ console.log('[mutate_memory] Observation enriched:', {
3511
+ observation_id: obs_id,
3512
+ keywords: result.extractedKeywords?.length || 0,
3513
+ tags: result.addedTags?.length || 0,
3514
+ links: result.createdLinks || 0
3515
+ });
3516
+ return JSON.stringify(result);
3517
+ }
3518
+ catch (error) {
3519
+ console.error('[mutate_memory] Error enriching observation:', error);
3520
+ return JSON.stringify({
3521
+ error: "Failed to enrich observation",
3522
+ details: error.message
3523
+ });
3524
+ }
3525
+ }
3526
+ if (action === "record_memory_access") {
3527
+ try {
3528
+ console.log('[mutate_memory] Recording memory access:', rest.observation_id);
3529
+ await this.getActivationService().recordAccess(rest.observation_id);
3530
+ console.log('[mutate_memory] Memory access recorded successfully');
3531
+ return JSON.stringify({
3532
+ success: true,
3533
+ observation_id: rest.observation_id,
3534
+ message: "Access recorded successfully"
3535
+ });
3536
+ }
3537
+ catch (error) {
3538
+ console.error('[mutate_memory] Error recording memory access:', error);
3539
+ return JSON.stringify({
3540
+ error: "Failed to record memory access",
3541
+ details: error.message
3542
+ });
3543
+ }
3544
+ }
3545
+ if (action === "prune_weak_memories") {
3546
+ try {
3547
+ const dryRun = rest.dry_run !== false; // Default to true
3548
+ console.log('[mutate_memory] Pruning weak memories:', {
3549
+ dry_run: dryRun,
3550
+ entity_id: rest.entity_id || 'all'
3551
+ });
3552
+ const result = await this.getActivationService().pruneWeakMemories(dryRun, rest.entity_id);
3553
+ console.log('[mutate_memory] Prune completed:', {
3554
+ pruned: result.pruned,
3555
+ preserved: result.preserved,
3556
+ candidates: result.candidates.length
3557
+ });
3558
+ return JSON.stringify(result);
3559
+ }
3560
+ catch (error) {
3561
+ console.error('[mutate_memory] Error pruning weak memories:', error);
3562
+ return JSON.stringify({
3563
+ error: "Failed to prune weak memories",
3564
+ details: error.message
3565
+ });
3566
+ }
3567
+ }
3568
+ if (action === "detect_conflicts") {
3569
+ try {
3570
+ console.log('[mutate_memory] Detecting conflicts for entity:', rest.entity_id);
3571
+ const conflicts = await this.getConflictService().detectConflicts(rest.entity_id);
3572
+ console.log('[mutate_memory] Conflict detection completed:', {
3573
+ entity_id: rest.entity_id,
3574
+ conflicts_found: conflicts.length
3575
+ });
3576
+ return JSON.stringify({
3577
+ entity_id: rest.entity_id,
3578
+ conflicts,
3579
+ count: conflicts.length
3580
+ });
3581
+ }
3582
+ catch (error) {
3583
+ console.error('[mutate_memory] Error detecting conflicts:', error);
3584
+ return JSON.stringify({
3585
+ error: "Failed to detect conflicts",
3586
+ details: error.message
3587
+ });
3588
+ }
3589
+ }
3590
+ if (action === "resolve_conflicts") {
3591
+ try {
3592
+ console.log('[mutate_memory] Resolving conflicts for entity:', rest.entity_id);
3593
+ const result = await this.getConflictService().resolveConflicts(rest.entity_id);
3594
+ console.log('[mutate_memory] Conflict resolution completed:', {
3595
+ entity_id: rest.entity_id,
3596
+ resolved: result.resolved_conflicts,
3597
+ invalidated: result.invalidated_observations.length,
3598
+ audit_trail: result.audit_observations.length
3599
+ });
3600
+ return JSON.stringify(result);
3601
+ }
3602
+ catch (error) {
3603
+ console.error('[mutate_memory] Error resolving conflicts:', error);
3604
+ return JSON.stringify({
3605
+ error: "Failed to resolve conflicts",
3606
+ details: error.message
3607
+ });
3608
+ }
3609
+ }
3319
3610
  return JSON.stringify({ error: "Unknown action" });
3320
3611
  },
3321
3612
  });
@@ -3441,10 +3732,48 @@ Validation: Invalid syntax or missing columns in inference rules will result in
3441
3732
  query: zod_1.z.string().describe("Search query for adaptive retrieval"),
3442
3733
  limit: zod_1.z.number().optional().default(10).describe("Maximum number of results"),
3443
3734
  }),
3735
+ zod_1.z.object({
3736
+ action: zod_1.z.literal("get_zettelkasten_stats"),
3737
+ }),
3738
+ zod_1.z.object({
3739
+ action: zod_1.z.literal("get_activation_stats"),
3740
+ entity_id: zod_1.z.string().optional().describe("Optional: Filter by entity ID"),
3741
+ }),
3742
+ zod_1.z.object({
3743
+ action: zod_1.z.literal("get_salience_stats"),
3744
+ }),
3745
+ zod_1.z.object({
3746
+ action: zod_1.z.literal("suggest_connections"),
3747
+ entity_id: zod_1.z.string().describe("Entity ID to suggest connections for"),
3748
+ max_suggestions: zod_1.z.number().optional().default(10).describe("Maximum number of suggestions"),
3749
+ min_confidence: zod_1.z.number().min(0).max(1).optional().default(0.5).describe("Minimum confidence threshold"),
3750
+ }),
3751
+ zod_1.z.object({
3752
+ action: zod_1.z.literal("spreading_activation"),
3753
+ query: zod_1.z.string().describe("Search query for spreading activation"),
3754
+ seed_top_k: zod_1.z.number().optional().default(5).describe("Number of seed nodes to start from"),
3755
+ limit: zod_1.z.number().optional().default(30).describe("Maximum number of results"),
3756
+ }),
3757
+ zod_1.z.object({
3758
+ action: zod_1.z.literal("qafd_search"),
3759
+ query: zod_1.z.string().describe("Search query for QAFD (Query-Aware Flow Diffusion)"),
3760
+ seed_top_k: zod_1.z.number().optional().default(5).describe("Number of vector seeds"),
3761
+ max_hops: zod_1.z.number().min(1).max(3).optional().default(2).describe("Maximum traversal depth"),
3762
+ damping_factor: zod_1.z.number().min(0).max(1).optional().default(0.85).describe("Flow diffusion damping factor"),
3763
+ min_score: zod_1.z.number().min(0).max(1).optional().default(0.05).describe("Minimum relevance threshold"),
3764
+ limit: zod_1.z.number().optional().default(10).describe("Maximum number of results"),
3765
+ }),
3766
+ zod_1.z.object({
3767
+ action: zod_1.z.literal("hierarchical_memory_query"),
3768
+ query: zod_1.z.string().describe("Search query for hierarchical memory"),
3769
+ entity_id: zod_1.z.string().optional().describe("Optional: Filter by entity ID"),
3770
+ levels: zod_1.z.array(zod_1.z.number().min(0).max(3)).optional().describe("Memory levels to query (0-3)"),
3771
+ limit: zod_1.z.number().optional().default(10).describe("Maximum number of results"),
3772
+ }),
3444
3773
  ]);
3445
3774
  const QueryMemoryParameters = zod_1.z.object({
3446
3775
  action: zod_1.z
3447
- .enum(["search", "advancedSearch", "context", "entity_details", "history", "graph_rag", "graph_walking", "agentic_search", "dynamic_fusion", "adaptive_retrieval"])
3776
+ .enum(["search", "advancedSearch", "context", "entity_details", "history", "graph_rag", "graph_walking", "agentic_search", "dynamic_fusion", "adaptive_retrieval", "get_zettelkasten_stats", "get_activation_stats", "get_salience_stats", "suggest_connections", "spreading_activation", "qafd_search", "hierarchical_memory_query"])
3448
3777
  .describe("Action (determines which fields are required)"),
3449
3778
  query: zod_1.z.string().optional().describe("Required for search/advancedSearch/context/graph_rag/graph_walking/agentic_search/dynamic_fusion/adaptive_retrieval"),
3450
3779
  limit: zod_1.z.number().optional().describe("Only for search/advancedSearch/graph_rag/graph_walking/dynamic_fusion/adaptive_retrieval"),
@@ -3481,6 +3810,11 @@ Supported actions:
3481
3810
  - 'agentic_search': Auto-Routing Search. Uses local LLM to analyze intent and routes the query automatically to the best strategy (Vector, Graph, or Community Summaries). Params: { query: string, limit?: number }.
3482
3811
  - 'adaptive_retrieval': GraphRAG-R1 inspired adaptive retrieval with Progressive Retrieval Attenuation (PRA) and Cost-Aware F1 (CAF) scoring. Automatically selects optimal strategy based on query complexity and historical performance. Params: { query: string, limit?: number }.
3483
3812
  - 'dynamic_fusion': Advanced multi-path fusion search combining Vector (HNSW), Sparse (keyword), FTS (full-text), and Graph traversal with configurable weights and strategies. Params: { query: string, config?: { vector?, sparse?, fts?, graph?, fusion? }, limit?: number }. Each path can be enabled/disabled and weighted independently. Fusion strategies: 'rrf' (Reciprocal Rank Fusion), 'weighted_sum', 'max', 'adaptive'. Returns results with path contribution details and performance stats.
3813
+ - 'get_zettelkasten_stats': Get Zettelkasten Memory Evolution statistics. No params required. Returns: { totalObservations, enrichedObservations, totalLinks, averageLinksPerNote, topKeywords, topTags, connectionTypes }.
3814
+ - 'get_activation_stats': Get ACT-R Memory Activation statistics. Params: { entity_id?: string }. Returns: { totalObservations, averageActivation, averageStrength, belowThreshold, aboveThreshold, distribution }.
3815
+ - 'get_salience_stats': Get Emotional Salience statistics. No params required. Returns: { totalObservations, withSalience, distribution, averageSalience, topKeywords }.
3816
+ - 'suggest_connections': Proactive connection suggestions for an entity. Params: { entity_id: string, max_suggestions?: number, min_confidence?: number }. Returns: { entity_id, suggestions: [{ entity_id, entity_name, entity_type, source, confidence, confidence_level, reason, metadata }], count }. Combines vector similarity, common neighbors, graph proximity, and inference strategies.
3817
+ - 'spreading_activation': SYNAPSE spreading activation search. Params: { query: string, seed_top_k?: number, limit?: number }. Returns: { scores: [{ entityId, activation, potential, source, hops }], iterations, converged, seedNodes }. Implements neural-inspired activation spreading with fan effect, lateral inhibition, and sigmoid activation.
3484
3818
 
3485
3819
  Notes: 'adaptive_retrieval' learns from usage and optimizes over time. 'dynamic_fusion' provides the most control and transparency over retrieval paths. 'agentic_search' is the most adaptive. 'context' is ideal for exploratory questions. 'search' and 'advancedSearch' are better for targeted fact retrieval.`,
3486
3820
  parameters: QueryMemoryParameters,
@@ -3748,6 +4082,214 @@ Notes: 'adaptive_retrieval' learns from usage and optimizes over time. 'dynamic_
3748
4082
  });
3749
4083
  }
3750
4084
  }
4085
+ if (input.action === "get_zettelkasten_stats") {
4086
+ try {
4087
+ console.log('[query_memory] Getting Zettelkasten stats');
4088
+ const stats = await this.getZettelkastenService().getEvolutionStats();
4089
+ console.log('[query_memory] Zettelkasten stats retrieved:', {
4090
+ totalObservations: stats.totalObservations,
4091
+ enrichedObservations: stats.enrichedObservations,
4092
+ totalLinks: stats.totalLinks,
4093
+ averageLinksPerNote: stats.averageLinksPerNote.toFixed(2)
4094
+ });
4095
+ return JSON.stringify(stats);
4096
+ }
4097
+ catch (error) {
4098
+ console.error('[query_memory] Error getting Zettelkasten stats:', error);
4099
+ return JSON.stringify({
4100
+ error: "Failed to get Zettelkasten stats",
4101
+ details: error.message
4102
+ });
4103
+ }
4104
+ }
4105
+ if (input.action === "get_activation_stats") {
4106
+ try {
4107
+ console.log('[query_memory] Getting ACT-R activation stats', input.entity_id ? `for entity ${input.entity_id}` : '(all entities)');
4108
+ const stats = await this.getActivationService().getActivationStats(input.entity_id);
4109
+ console.log('[query_memory] Activation stats retrieved:', {
4110
+ totalObservations: stats.totalObservations,
4111
+ averageActivation: stats.averageActivation.toFixed(3),
4112
+ belowThreshold: stats.belowThreshold,
4113
+ aboveThreshold: stats.aboveThreshold
4114
+ });
4115
+ return JSON.stringify(stats);
4116
+ }
4117
+ catch (error) {
4118
+ console.error('[query_memory] Error getting activation stats:', error);
4119
+ return JSON.stringify({
4120
+ error: "Failed to get activation stats",
4121
+ details: error.message
4122
+ });
4123
+ }
4124
+ }
4125
+ if (input.action === "get_salience_stats") {
4126
+ try {
4127
+ console.log('[query_memory] Getting Emotional Salience stats');
4128
+ const stats = await this.getSalienceService().getSalienceStats();
4129
+ console.log('[query_memory] Salience stats retrieved:', {
4130
+ totalObservations: stats.totalObservations,
4131
+ withSalience: stats.withSalience,
4132
+ averageSalience: stats.averageSalience.toFixed(3)
4133
+ });
4134
+ return JSON.stringify(stats);
4135
+ }
4136
+ catch (error) {
4137
+ console.error('[query_memory] Error getting salience stats:', error);
4138
+ return JSON.stringify({
4139
+ error: "Failed to get salience stats",
4140
+ details: error.message
4141
+ });
4142
+ }
4143
+ }
4144
+ if (input.action === "suggest_connections") {
4145
+ try {
4146
+ console.log('[query_memory] Suggesting connections for entity:', input.entity_id);
4147
+ const service = this.getSuggestionsService();
4148
+ // Update config if custom parameters provided
4149
+ if (input.max_suggestions || input.min_confidence) {
4150
+ service.updateConfig({
4151
+ maxSuggestions: input.max_suggestions,
4152
+ minConfidence: input.min_confidence
4153
+ });
4154
+ }
4155
+ const suggestions = await service.suggestConnections(input.entity_id);
4156
+ console.log('[query_memory] Found', suggestions.length, 'connection suggestions');
4157
+ return JSON.stringify({
4158
+ entity_id: input.entity_id,
4159
+ suggestions,
4160
+ count: suggestions.length
4161
+ });
4162
+ }
4163
+ catch (error) {
4164
+ console.error('[query_memory] Error suggesting connections:', error);
4165
+ return JSON.stringify({
4166
+ error: "Failed to suggest connections",
4167
+ details: error.message
4168
+ });
4169
+ }
4170
+ }
4171
+ if (input.action === "spreading_activation") {
4172
+ try {
4173
+ console.log('[query_memory] Spreading activation search:', input.query);
4174
+ const result = await this.getSpreadingService().spreadActivation(input.query, input.seed_top_k || 5);
4175
+ // Limit results if requested
4176
+ const limitedScores = input.limit
4177
+ ? result.scores.slice(0, input.limit)
4178
+ : result.scores;
4179
+ console.log('[query_memory] Spreading activation completed:', {
4180
+ totalScores: result.scores.length,
4181
+ returned: limitedScores.length,
4182
+ iterations: result.iterations,
4183
+ converged: result.converged
4184
+ });
4185
+ return JSON.stringify({
4186
+ scores: limitedScores,
4187
+ iterations: result.iterations,
4188
+ converged: result.converged,
4189
+ seedNodes: result.seedNodes,
4190
+ totalResults: result.scores.length
4191
+ });
4192
+ }
4193
+ catch (error) {
4194
+ console.error('[query_memory] Error in spreading activation:', error);
4195
+ return JSON.stringify({
4196
+ error: "Failed to perform spreading activation",
4197
+ details: error.message
4198
+ });
4199
+ }
4200
+ }
4201
+ if (input.action === "qafd_search") {
4202
+ try {
4203
+ console.log('[query_memory] QAFD search:', input.query);
4204
+ const result = await this.getQueryAwareTraversal().hybridSearch(input.query, {
4205
+ seedTopK: input.seed_top_k || 5,
4206
+ maxHops: input.max_hops || 2,
4207
+ dampingFactor: input.damping_factor || 0.85,
4208
+ minScore: input.min_score || 0.05,
4209
+ topK: input.limit || 10
4210
+ });
4211
+ console.log('[query_memory] QAFD search completed:', {
4212
+ totalResults: result.length
4213
+ });
4214
+ return JSON.stringify({
4215
+ results: result,
4216
+ count: result.length
4217
+ });
4218
+ }
4219
+ catch (error) {
4220
+ console.error('[query_memory] Error in QAFD search:', error);
4221
+ return JSON.stringify({
4222
+ error: "Failed to perform QAFD search",
4223
+ details: error.message
4224
+ });
4225
+ }
4226
+ }
4227
+ if (input.action === "hierarchical_memory_query") {
4228
+ try {
4229
+ console.log('[query_memory] Hierarchical memory query:', input.query);
4230
+ // Build query filter for memory levels
4231
+ let levelFilter = '';
4232
+ if (input.levels && input.levels.length > 0) {
4233
+ const levelConditions = input.levels.map((l) => `memory_level = ${l}`).join(' or ');
4234
+ levelFilter = `, (${levelConditions})`;
4235
+ }
4236
+ // Build entity filter
4237
+ let entityFilter = '';
4238
+ if (input.entity_id) {
4239
+ entityFilter = `, entity_id = $entity_id`;
4240
+ }
4241
+ // Query observations with level filtering
4242
+ const queryEmbedding = await this.embeddingService.embed(input.query);
4243
+ const datalog = `
4244
+ ?[id, entity_id, text, memory_level, dist] :=
4245
+ ~observation:semantic{
4246
+ id |
4247
+ query: vec($embedding),
4248
+ k: $limit,
4249
+ ef: 100,
4250
+ bind_distance: dist
4251
+ },
4252
+ *observation{id, entity_id, text, metadata, @ "NOW"},
4253
+ memory_level = get(metadata, "memory_level", 0)
4254
+ ${levelFilter}
4255
+ ${entityFilter}
4256
+
4257
+ :order dist
4258
+ `;
4259
+ const params = {
4260
+ embedding: queryEmbedding,
4261
+ limit: input.limit || 10
4262
+ };
4263
+ if (input.entity_id) {
4264
+ params.entity_id = input.entity_id;
4265
+ }
4266
+ const result = await this.db.run(datalog, params);
4267
+ const observations = result.rows.map((r) => ({
4268
+ id: r[0],
4269
+ entity_id: r[1],
4270
+ text: r[2],
4271
+ memory_level: r[3],
4272
+ distance: r[4]
4273
+ }));
4274
+ console.log('[query_memory] Hierarchical memory query completed:', {
4275
+ totalResults: observations.length
4276
+ });
4277
+ return JSON.stringify({
4278
+ results: observations,
4279
+ count: observations.length,
4280
+ query: input.query,
4281
+ levels: input.levels || [0, 1, 2, 3],
4282
+ entity_id: input.entity_id
4283
+ });
4284
+ }
4285
+ catch (error) {
4286
+ console.error('[query_memory] Error in hierarchical memory query:', error);
4287
+ return JSON.stringify({
4288
+ error: "Failed to perform hierarchical memory query",
4289
+ details: error.message
4290
+ });
4291
+ }
4292
+ }
3751
4293
  if (input.action === "graph_rag") {
3752
4294
  if (!input.query || input.query.trim().length === 0) {
3753
4295
  return JSON.stringify({ error: "Search query must not be empty." });
@@ -3894,16 +4436,28 @@ Notes: 'adaptive_retrieval' learns from usage and optimizes over time. 'dynamic_
3894
4436
  zod_1.z.object({
3895
4437
  action: zod_1.z.literal("hnsw_clusters"),
3896
4438
  }),
4439
+ zod_1.z.object({
4440
+ action: zod_1.z.literal("discover_logical_edges"),
4441
+ entity_id: zod_1.z.string().describe("ID of the entity to discover logical edges for"),
4442
+ }),
4443
+ zod_1.z.object({
4444
+ action: zod_1.z.literal("materialize_logical_edges"),
4445
+ entity_id: zod_1.z.string().describe("ID of the entity to materialize logical edges for"),
4446
+ }),
4447
+ zod_1.z.object({
4448
+ action: zod_1.z.literal("detect_temporal_patterns"),
4449
+ entity_id: zod_1.z.string().describe("ID of the entity to detect temporal patterns for"),
4450
+ }),
3897
4451
  ]);
3898
4452
  const AnalyzeGraphParameters = zod_1.z.object({
3899
4453
  action: zod_1.z
3900
- .enum(["explore", "communities", "pagerank", "betweenness", "hits", "connected_components", "shortest_path", "bridge_discovery", "infer_relations", "get_relation_evolution", "semantic_walk", "hnsw_clusters"])
4454
+ .enum(["explore", "communities", "pagerank", "betweenness", "hits", "connected_components", "shortest_path", "bridge_discovery", "infer_relations", "get_relation_evolution", "semantic_walk", "hnsw_clusters", "discover_logical_edges", "materialize_logical_edges", "detect_temporal_patterns"])
3901
4455
  .describe("Action (determines which fields are required)"),
3902
4456
  start_entity: zod_1.z.string().optional().describe("Required for explore/shortest_path/semantic_walk (Start entity ID)"),
3903
4457
  end_entity: zod_1.z.string().optional().describe("Optional for explore / required for shortest_path"),
3904
4458
  max_hops: zod_1.z.number().optional().describe("Optional for explore"),
3905
4459
  relation_types: zod_1.z.array(zod_1.z.string()).optional().describe("Optional for explore"),
3906
- entity_id: zod_1.z.string().optional().describe("Required for infer_relations"),
4460
+ entity_id: zod_1.z.string().optional().describe("Required for infer_relations/discover_logical_edges/materialize_logical_edges/detect_temporal_patterns"),
3907
4461
  from_id: zod_1.z.string().optional().describe("Required for get_relation_evolution"),
3908
4462
  to_id: zod_1.z.string().optional().describe("Optional for get_relation_evolution"),
3909
4463
  max_depth: zod_1.z.number().optional().describe("Optional for semantic_walk"),
@@ -3926,7 +4480,10 @@ Supported actions:
3926
4480
  - 'infer_relations': Starts the inference engine for an entity. Params: { entity_id: string }.
3927
4481
  - 'get_relation_evolution': Tracks the temporal evolution of relationships. Params: { from_id: string, to_id?: string }.
3928
4482
  - 'semantic_walk': Performs a recursive "Graph Walk" that follows both explicit relationships and semantic similarity. Params: { start_entity: string, max_depth?: number, min_similarity?: number }.
3929
- - 'hnsw_clusters': Analyzes clusters directly on the HNSW graph (Layer 0). Extremely fast as no vector calculations are needed.`,
4483
+ - 'hnsw_clusters': Analyzes clusters directly on the HNSW graph (Layer 0). Extremely fast as no vector calculations are needed.
4484
+ - 'discover_logical_edges': Discovers implicit logical edges for an entity (same category, type, hierarchical, contextual, transitive). Params: { entity_id: string }.
4485
+ - 'materialize_logical_edges': Materializes discovered logical edges as actual relationships in the graph. Params: { entity_id: string }.
4486
+ - 'detect_temporal_patterns': Detects temporal patterns in entity observations (periodic, trending, seasonal). Params: { entity_id: string }.`,
3930
4487
  parameters: AnalyzeGraphParameters,
3931
4488
  execute: async (args) => {
3932
4489
  await this.initPromise;
@@ -4181,6 +4738,98 @@ Supported actions:
4181
4738
  return JSON.stringify({ error: error.message || "Error during HNSW Cluster Analysis" });
4182
4739
  }
4183
4740
  }
4741
+ if (input.action === "discover_logical_edges") {
4742
+ try {
4743
+ if (!input.entity_id) {
4744
+ return JSON.stringify({ error: "entity_id is required for discover_logical_edges" });
4745
+ }
4746
+ const edges = await this.getLogicalEdgesService().discoverLogicalEdges(input.entity_id);
4747
+ return JSON.stringify({
4748
+ entity_id: input.entity_id,
4749
+ edge_count: edges.length,
4750
+ edges
4751
+ });
4752
+ }
4753
+ catch (error) {
4754
+ return JSON.stringify({ error: error.message || "Error discovering logical edges" });
4755
+ }
4756
+ }
4757
+ if (input.action === "materialize_logical_edges") {
4758
+ try {
4759
+ if (!input.entity_id) {
4760
+ return JSON.stringify({ error: "entity_id is required for materialize_logical_edges" });
4761
+ }
4762
+ const count = await this.getLogicalEdgesService().materializeLogicalEdges(input.entity_id);
4763
+ return JSON.stringify({
4764
+ entity_id: input.entity_id,
4765
+ materialized_count: count,
4766
+ status: `${count} logical edges materialized as relationships`
4767
+ });
4768
+ }
4769
+ catch (error) {
4770
+ return JSON.stringify({ error: error.message || "Error materializing logical edges" });
4771
+ }
4772
+ }
4773
+ if (input.action === "detect_temporal_patterns") {
4774
+ try {
4775
+ if (!input.entity_id) {
4776
+ return JSON.stringify({ error: "entity_id is required for detect_temporal_patterns" });
4777
+ }
4778
+ // Check if entity exists
4779
+ const entityRes = await this.db.run('?[name] := *entity{id: $id, name, @ "NOW"}', { id: input.entity_id });
4780
+ if (entityRes.rows.length === 0) {
4781
+ return JSON.stringify({ error: `Entity with ID '${input.entity_id}' not found` });
4782
+ }
4783
+ // Get observations for the entity
4784
+ const obsRes = await this.db.run(`
4785
+ ?[id, text, created_at] :=
4786
+ *observation{id, entity_id, text, created_at, @ "NOW"},
4787
+ entity_id = $entity_id
4788
+ `, { entity_id: input.entity_id });
4789
+ if (obsRes.rows.length === 0) {
4790
+ return JSON.stringify({
4791
+ entity_id: input.entity_id,
4792
+ patterns: [],
4793
+ message: "No observations found for temporal pattern detection"
4794
+ });
4795
+ }
4796
+ // Simple temporal pattern detection
4797
+ const observations = obsRes.rows.map((r) => ({
4798
+ id: r[0],
4799
+ text: r[1],
4800
+ timestamp: r[2][0] / 1000 // Convert microseconds to milliseconds
4801
+ }));
4802
+ // Sort by timestamp
4803
+ observations.sort((a, b) => a.timestamp - b.timestamp);
4804
+ const patterns = [];
4805
+ // Detect trending (increasing frequency over time)
4806
+ if (observations.length >= 3) {
4807
+ const timeSpan = observations[observations.length - 1].timestamp - observations[0].timestamp;
4808
+ const avgInterval = timeSpan / (observations.length - 1);
4809
+ if (avgInterval < 7 * 24 * 60 * 60 * 1000) { // Less than a week
4810
+ patterns.push({
4811
+ type: "trending",
4812
+ confidence: 0.8,
4813
+ description: "High frequency of observations detected",
4814
+ observation_count: observations.length,
4815
+ time_span_days: (timeSpan / (24 * 60 * 60 * 1000)).toFixed(1)
4816
+ });
4817
+ }
4818
+ }
4819
+ return JSON.stringify({
4820
+ entity_id: input.entity_id,
4821
+ observation_count: observations.length,
4822
+ patterns,
4823
+ time_range: {
4824
+ start: new Date(observations[0].timestamp).toISOString(),
4825
+ end: new Date(observations[observations.length - 1].timestamp).toISOString()
4826
+ }
4827
+ });
4828
+ }
4829
+ catch (error) {
4830
+ return JSON.stringify({ error: error.message || "Error detecting temporal patterns" });
4831
+ }
4832
+ }
4184
4833
  return JSON.stringify({ error: "Unknown action" });
4185
4834
  },
4186
4835
  });
@@ -4248,10 +4897,19 @@ Supported actions:
4248
4897
  entity_id: zod_1.z.string().optional().describe("Entity ID to compact"),
4249
4898
  model: zod_1.z.string().optional().default("demyagent-4b-i1:Q6_K"),
4250
4899
  }),
4900
+ zod_1.z.object({
4901
+ action: zod_1.z.literal("compress_memory_levels"),
4902
+ entity_id: zod_1.z.string().describe("Entity ID to compress memory levels for"),
4903
+ level: zod_1.z.number().min(0).max(3).describe("Memory level to compress (0-3: L0_RAW, L1_SESSION, L2_WEEKLY, L3_MONTHLY)"),
4904
+ }),
4905
+ zod_1.z.object({
4906
+ action: zod_1.z.literal("analyze_memory_distribution"),
4907
+ entity_id: zod_1.z.string().describe("Entity ID to analyze memory distribution for"),
4908
+ }),
4251
4909
  ]);
4252
4910
  const ManageSystemParameters = zod_1.z.object({
4253
4911
  action: zod_1.z
4254
- .enum(["health", "metrics", "export_memory", "import_memory", "snapshot_create", "snapshot_list", "snapshot_diff", "cleanup", "defrag", "reflect", "clear_memory", "summarize_communities", "compact"])
4912
+ .enum(["health", "metrics", "export_memory", "import_memory", "snapshot_create", "snapshot_list", "snapshot_diff", "cleanup", "defrag", "reflect", "clear_memory", "summarize_communities", "compact", "compress_memory_levels", "analyze_memory_distribution"])
4255
4913
  .describe("Action (determines which fields are required)"),
4256
4914
  format: zod_1.z.enum(["json", "markdown", "obsidian"]).optional().describe("Export format (for export_memory)"),
4257
4915
  includeMetadata: zod_1.z.boolean().optional().describe("Include metadata (for export_memory)"),
@@ -4273,9 +4931,10 @@ Supported actions:
4273
4931
  similarity_threshold: zod_1.z.number().optional().describe("Optional for defrag (0.8-1.0, default 0.95)"),
4274
4932
  min_island_size: zod_1.z.number().optional().describe("Optional for defrag (1-10, default 3)"),
4275
4933
  model: zod_1.z.string().optional().describe("Optional for cleanup/reflect/summarize_communities"),
4276
- entity_id: zod_1.z.string().optional().describe("Optional for reflect"),
4934
+ entity_id: zod_1.z.string().optional().describe("Optional for reflect, required for compress_memory_levels/analyze_memory_distribution"),
4277
4935
  min_community_size: zod_1.z.number().optional().describe("Optional for summarize_communities"),
4278
4936
  mode: zod_1.z.enum(["summary", "discovery"]).optional().describe("Optional for reflect"),
4937
+ level: zod_1.z.number().optional().describe("Required for compress_memory_levels (0-3: L0_RAW, L1_SESSION, L2_WEEKLY, L3_MONTHLY)"),
4279
4938
  });
4280
4939
  this.mcp.addTool({
4281
4940
  name: "manage_system",
@@ -4303,7 +4962,9 @@ Supported actions:
4303
4962
  * With confirm=true: Executes defragmentation and returns statistics.
4304
4963
  - 'reflect': Reflection service. Analyzes memory for contradictions and insights. Params: { entity_id?: string, model?: string }.
4305
4964
  - 'clear_memory': Resets the entire database. Params: { confirm: boolean (must be true) }.
4306
- - 'summarize_communities': Hierarchical GraphRAG. Generates summaries for entity clusters. Params: { model?: string, min_community_size?: number }.`,
4965
+ - 'summarize_communities': Hierarchical GraphRAG. Generates summaries for entity clusters. Params: { model?: string, min_community_size?: number }.
4966
+ - 'compress_memory_levels': Hierarchical memory compression. Compresses observations at a specific memory level using LLM summarization. Params: { entity_id: string, level: number (0-3) }.
4967
+ - 'analyze_memory_distribution': Analyzes memory distribution across hierarchical levels. Params: { entity_id: string }.`,
4307
4968
  parameters: ManageSystemParameters,
4308
4969
  execute: async (args) => {
4309
4970
  await this.initPromise;
@@ -4573,6 +5234,62 @@ Supported actions:
4573
5234
  return JSON.stringify({ error: error.message || "Error during compaction" });
4574
5235
  }
4575
5236
  }
5237
+ if (input.action === "compress_memory_levels") {
5238
+ try {
5239
+ if (!input.entity_id) {
5240
+ return JSON.stringify({ error: "entity_id is required for compress_memory_levels" });
5241
+ }
5242
+ if (input.level === undefined || input.level === null) {
5243
+ return JSON.stringify({ error: "level is required (0-3: L0_RAW, L1_SESSION, L2_WEEKLY, L3_MONTHLY)" });
5244
+ }
5245
+ if (input.level < 0 || input.level > 3) {
5246
+ return JSON.stringify({ error: "level must be between 0 and 3" });
5247
+ }
5248
+ const result = await this.getHierarchicalMemoryService().compressMemoryLevel(input.entity_id, input.level);
5249
+ if (!result) {
5250
+ return JSON.stringify({
5251
+ entity_id: input.entity_id,
5252
+ level: input.level,
5253
+ status: "no_compression_needed",
5254
+ message: "Not enough observations for compression at this level"
5255
+ });
5256
+ }
5257
+ return JSON.stringify({
5258
+ entity_id: input.entity_id,
5259
+ level: result.level,
5260
+ compressed_observations: result.compressed_observations,
5261
+ summary_id: result.summary_id,
5262
+ preserved_count: result.preserved_observations.length,
5263
+ deleted_count: result.deleted_observations.length,
5264
+ status: "compression_completed"
5265
+ });
5266
+ }
5267
+ catch (error) {
5268
+ return JSON.stringify({ error: error.message || "Error compressing memory levels" });
5269
+ }
5270
+ }
5271
+ if (input.action === "analyze_memory_distribution") {
5272
+ try {
5273
+ if (!input.entity_id) {
5274
+ return JSON.stringify({ error: "entity_id is required for analyze_memory_distribution" });
5275
+ }
5276
+ const stats = await this.getHierarchicalMemoryService().getMemoryStats(input.entity_id);
5277
+ return JSON.stringify({
5278
+ entity_id: input.entity_id,
5279
+ total_observations: stats.total_observations,
5280
+ distribution: {
5281
+ L0_RAW: stats.by_level[0] || 0,
5282
+ L1_SESSION: stats.by_level[1] || 0,
5283
+ L2_WEEKLY: stats.by_level[2] || 0,
5284
+ L3_MONTHLY: stats.by_level[3] || 0
5285
+ },
5286
+ avg_importance: stats.avg_importance
5287
+ });
5288
+ }
5289
+ catch (error) {
5290
+ return JSON.stringify({ error: error.message || "Error analyzing memory distribution" });
5291
+ }
5292
+ }
4576
5293
  return JSON.stringify({ error: "Unknown action" });
4577
5294
  },
4578
5295
  });