mcp-adr-analysis-server 2.0.21 → 2.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 +94 -839
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/config/ai-config.d.ts.map +1 -1
- package/dist/src/config/ai-config.js +9 -9
- package/dist/src/config/ai-config.js.map +1 -1
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1594 -837
- package/dist/src/index.js.map +1 -1
- package/dist/src/prompts/analysis-prompts.d.ts +4 -1
- package/dist/src/prompts/analysis-prompts.d.ts.map +1 -1
- package/dist/src/prompts/analysis-prompts.js +61 -30
- package/dist/src/prompts/analysis-prompts.js.map +1 -1
- package/dist/src/prompts/index.d.ts +17 -1
- package/dist/src/prompts/index.d.ts.map +1 -1
- package/dist/src/prompts/index.js +325 -1
- package/dist/src/prompts/index.js.map +1 -1
- package/dist/src/prompts/rule-generation-prompts.d.ts +5 -1
- package/dist/src/prompts/rule-generation-prompts.d.ts.map +1 -1
- package/dist/src/prompts/rule-generation-prompts.js +5 -1
- package/dist/src/prompts/rule-generation-prompts.js.map +1 -1
- package/dist/src/prompts/security-prompts.d.ts +6 -1
- package/dist/src/prompts/security-prompts.d.ts.map +1 -1
- package/dist/src/prompts/security-prompts.js +6 -1
- package/dist/src/prompts/security-prompts.js.map +1 -1
- package/dist/src/prompts/todo-prompts.d.ts +49 -0
- package/dist/src/prompts/todo-prompts.d.ts.map +1 -0
- package/dist/src/prompts/todo-prompts.js +824 -0
- package/dist/src/prompts/todo-prompts.js.map +1 -0
- package/dist/src/resources/index.d.ts.map +1 -1
- package/dist/src/resources/index.js +9 -7
- package/dist/src/resources/index.js.map +1 -1
- package/dist/src/tools/adr-bootstrap-validation-tool.d.ts +47 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.d.ts.map +1 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.js +979 -0
- package/dist/src/tools/adr-bootstrap-validation-tool.js.map +1 -0
- package/dist/src/tools/adr-suggestion-tool.d.ts +1 -0
- package/dist/src/tools/adr-suggestion-tool.d.ts.map +1 -1
- package/dist/src/tools/adr-suggestion-tool.js +330 -32
- package/dist/src/tools/adr-suggestion-tool.js.map +1 -1
- package/dist/src/tools/content-masking-tool.d.ts +5 -0
- package/dist/src/tools/content-masking-tool.d.ts.map +1 -1
- package/dist/src/tools/content-masking-tool.js +631 -28
- package/dist/src/tools/content-masking-tool.js.map +1 -1
- package/dist/src/tools/deployment-readiness-tool.d.ts +8 -5
- package/dist/src/tools/deployment-readiness-tool.d.ts.map +1 -1
- package/dist/src/tools/deployment-readiness-tool.js +1017 -93
- package/dist/src/tools/deployment-readiness-tool.js.map +1 -1
- package/dist/src/tools/environment-analysis-tool.d.ts +4 -0
- package/dist/src/tools/environment-analysis-tool.d.ts.map +1 -1
- package/dist/src/tools/environment-analysis-tool.js +568 -24
- package/dist/src/tools/environment-analysis-tool.js.map +1 -1
- package/dist/src/tools/interactive-adr-planning-tool.d.ts.map +1 -1
- package/dist/src/tools/interactive-adr-planning-tool.js +270 -108
- package/dist/src/tools/interactive-adr-planning-tool.js.map +1 -1
- package/dist/src/tools/mcp-planning-tool.d.ts.map +1 -1
- package/dist/src/tools/mcp-planning-tool.js +316 -183
- package/dist/src/tools/mcp-planning-tool.js.map +1 -1
- package/dist/src/tools/memory-loading-tool.d.ts +60 -0
- package/dist/src/tools/memory-loading-tool.d.ts.map +1 -0
- package/dist/src/tools/memory-loading-tool.js +457 -0
- package/dist/src/tools/memory-loading-tool.js.map +1 -0
- package/dist/src/tools/research-integration-tool.d.ts +4 -1
- package/dist/src/tools/research-integration-tool.d.ts.map +1 -1
- package/dist/src/tools/research-integration-tool.js +5 -1
- package/dist/src/tools/research-integration-tool.js.map +1 -1
- package/dist/src/tools/review-existing-adrs-tool.d.ts +19 -0
- package/dist/src/tools/review-existing-adrs-tool.d.ts.map +1 -0
- package/dist/src/tools/review-existing-adrs-tool.js +1204 -0
- package/dist/src/tools/review-existing-adrs-tool.js.map +1 -0
- package/dist/src/tools/rule-generation-tool.d.ts +2 -0
- package/dist/src/tools/rule-generation-tool.d.ts.map +1 -1
- package/dist/src/tools/rule-generation-tool.js +274 -33
- package/dist/src/tools/rule-generation-tool.js.map +1 -1
- package/dist/src/tools/smart-git-push-tool-v2.d.ts +4 -0
- package/dist/src/tools/smart-git-push-tool-v2.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool-v2.js +423 -229
- package/dist/src/tools/smart-git-push-tool-v2.js.map +1 -1
- package/dist/src/tools/smart-git-push-tool.d.ts +3 -0
- package/dist/src/tools/smart-git-push-tool.d.ts.map +1 -1
- package/dist/src/tools/smart-git-push-tool.js +241 -235
- package/dist/src/tools/smart-git-push-tool.js.map +1 -1
- package/dist/src/tools/tool-chain-orchestrator.d.ts +2 -2
- package/dist/src/tools/tool-chain-orchestrator.d.ts.map +1 -1
- package/dist/src/tools/tool-chain-orchestrator.js +152 -93
- package/dist/src/tools/tool-chain-orchestrator.js.map +1 -1
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts +22 -13
- package/dist/src/tools/troubleshoot-guided-workflow-tool.d.ts.map +1 -1
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js +660 -28
- package/dist/src/tools/troubleshoot-guided-workflow-tool.js.map +1 -1
- package/dist/src/types/ape-framework.d.ts +93 -0
- package/dist/src/types/ape-framework.d.ts.map +1 -1
- package/dist/src/types/ape-framework.js +10 -10
- package/dist/src/types/ape-framework.js.map +1 -1
- package/dist/src/types/enhanced-errors.d.ts +173 -0
- package/dist/src/types/enhanced-errors.d.ts.map +1 -0
- package/dist/src/types/enhanced-errors.js +668 -0
- package/dist/src/types/enhanced-errors.js.map +1 -0
- package/dist/src/types/index.d.ts +129 -3
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +15 -0
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/knowledge-generation.d.ts +78 -0
- package/dist/src/types/knowledge-generation.d.ts.map +1 -1
- package/dist/src/types/knowledge-generation.js +10 -6
- package/dist/src/types/knowledge-generation.js.map +1 -1
- package/dist/src/types/knowledge-graph-schemas.d.ts +92 -0
- package/dist/src/types/knowledge-graph-schemas.d.ts.map +1 -1
- package/dist/src/types/knowledge-graph-schemas.js +45 -17
- package/dist/src/types/knowledge-graph-schemas.js.map +1 -1
- package/dist/src/types/memory-entities.d.ts +7209 -0
- package/dist/src/types/memory-entities.d.ts.map +1 -0
- package/dist/src/types/memory-entities.js +395 -0
- package/dist/src/types/memory-entities.js.map +1 -0
- package/dist/src/types/reflexion-framework.d.ts +44 -0
- package/dist/src/types/reflexion-framework.d.ts.map +1 -1
- package/dist/src/types/reflexion-framework.js +7 -7
- package/dist/src/types/reflexion-framework.js.map +1 -1
- package/dist/src/types/todo-json-schemas.d.ts +7 -653
- package/dist/src/types/todo-json-schemas.d.ts.map +1 -1
- package/dist/src/types/todo-json-schemas.js +6 -110
- package/dist/src/types/todo-json-schemas.js.map +1 -1
- package/dist/src/types/tool-arguments.d.ts +224 -0
- package/dist/src/types/tool-arguments.d.ts.map +1 -0
- package/dist/src/types/tool-arguments.js +6 -0
- package/dist/src/types/tool-arguments.js.map +1 -0
- package/dist/src/utils/actual-file-operations.d.ts.map +1 -1
- package/dist/src/utils/actual-file-operations.js +25 -9
- package/dist/src/utils/actual-file-operations.js.map +1 -1
- package/dist/src/utils/adr-discovery.d.ts +33 -1
- package/dist/src/utils/adr-discovery.d.ts.map +1 -1
- package/dist/src/utils/adr-discovery.js +14 -9
- package/dist/src/utils/adr-discovery.js.map +1 -1
- package/dist/src/utils/adr-suggestions.d.ts +72 -0
- package/dist/src/utils/adr-suggestions.d.ts.map +1 -1
- package/dist/src/utils/adr-suggestions.js +6 -0
- package/dist/src/utils/adr-suggestions.js.map +1 -1
- package/dist/src/utils/cache.d.ts.map +1 -1
- package/dist/src/utils/cache.js +22 -19
- package/dist/src/utils/cache.js.map +1 -1
- package/dist/src/utils/chain-of-thought-template.d.ts +13 -1
- package/dist/src/utils/chain-of-thought-template.d.ts.map +1 -1
- package/dist/src/utils/chain-of-thought-template.js +31 -22
- package/dist/src/utils/chain-of-thought-template.js.map +1 -1
- package/dist/src/utils/config.d.ts.map +1 -1
- package/dist/src/utils/config.js +1 -0
- package/dist/src/utils/config.js.map +1 -1
- package/dist/src/utils/directory-compat.js +1 -1
- package/dist/src/utils/directory-compat.js.map +1 -1
- package/dist/src/utils/enhanced-logging.d.ts +195 -0
- package/dist/src/utils/enhanced-logging.d.ts.map +1 -0
- package/dist/src/utils/enhanced-logging.js +449 -0
- package/dist/src/utils/enhanced-logging.js.map +1 -0
- package/dist/src/utils/file-system.d.ts +182 -34
- package/dist/src/utils/file-system.d.ts.map +1 -1
- package/dist/src/utils/file-system.js +648 -774
- package/dist/src/utils/file-system.js.map +1 -1
- package/dist/src/utils/gitleaks-detector.d.ts +50 -0
- package/dist/src/utils/gitleaks-detector.d.ts.map +1 -0
- package/dist/src/utils/gitleaks-detector.js +334 -0
- package/dist/src/utils/gitleaks-detector.js.map +1 -0
- package/dist/src/utils/interactive-approval.d.ts +1 -1
- package/dist/src/utils/interactive-approval.d.ts.map +1 -1
- package/dist/src/utils/interactive-approval.js +22 -17
- package/dist/src/utils/interactive-approval.js.map +1 -1
- package/dist/src/utils/json-safe.d.ts.map +1 -1
- package/dist/src/utils/json-safe.js +9 -6
- package/dist/src/utils/json-safe.js.map +1 -1
- package/dist/src/utils/knowledge-graph-manager.d.ts +37 -1
- package/dist/src/utils/knowledge-graph-manager.d.ts.map +1 -1
- package/dist/src/utils/knowledge-graph-manager.js +315 -70
- package/dist/src/utils/knowledge-graph-manager.js.map +1 -1
- package/dist/src/utils/location-filter.d.ts +35 -10
- package/dist/src/utils/location-filter.d.ts.map +1 -1
- package/dist/src/utils/location-filter.js +360 -94
- package/dist/src/utils/location-filter.js.map +1 -1
- package/dist/src/utils/mcp-response-validator.d.ts.map +1 -1
- package/dist/src/utils/mcp-response-validator.js +34 -19
- package/dist/src/utils/mcp-response-validator.js.map +1 -1
- package/dist/src/utils/memory-entity-manager.d.ts +134 -0
- package/dist/src/utils/memory-entity-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-entity-manager.js +851 -0
- package/dist/src/utils/memory-entity-manager.js.map +1 -0
- package/dist/src/utils/memory-health-scoring.d.ts +125 -0
- package/dist/src/utils/memory-health-scoring.d.ts.map +1 -0
- package/dist/src/utils/memory-health-scoring.js +277 -0
- package/dist/src/utils/memory-health-scoring.js.map +1 -0
- package/dist/src/utils/memory-migration-manager.d.ts +93 -0
- package/dist/src/utils/memory-migration-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-migration-manager.js +842 -0
- package/dist/src/utils/memory-migration-manager.js.map +1 -0
- package/dist/src/utils/memory-relationship-mapper.d.ts +87 -0
- package/dist/src/utils/memory-relationship-mapper.d.ts.map +1 -0
- package/dist/src/utils/memory-relationship-mapper.js +482 -0
- package/dist/src/utils/memory-relationship-mapper.js.map +1 -0
- package/dist/src/utils/memory-rollback-manager.d.ts +105 -0
- package/dist/src/utils/memory-rollback-manager.d.ts.map +1 -0
- package/dist/src/utils/memory-rollback-manager.js +575 -0
- package/dist/src/utils/memory-rollback-manager.js.map +1 -0
- package/dist/src/utils/memory-transformation.d.ts +59 -0
- package/dist/src/utils/memory-transformation.d.ts.map +1 -0
- package/dist/src/utils/memory-transformation.js +742 -0
- package/dist/src/utils/memory-transformation.js.map +1 -0
- package/dist/src/utils/output-masking.d.ts +12 -0
- package/dist/src/utils/output-masking.d.ts.map +1 -1
- package/dist/src/utils/output-masking.js +5 -0
- package/dist/src/utils/output-masking.js.map +1 -1
- package/dist/src/utils/prompt-execution.d.ts.map +1 -1
- package/dist/src/utils/prompt-execution.js +72 -51
- package/dist/src/utils/prompt-execution.js.map +1 -1
- package/dist/src/utils/release-readiness-detector.d.ts.map +1 -1
- package/dist/src/utils/release-readiness-detector.js +43 -42
- package/dist/src/utils/release-readiness-detector.js.map +1 -1
- package/dist/src/utils/research-integration.d.ts.map +1 -1
- package/dist/src/utils/research-integration.js +73 -44
- package/dist/src/utils/research-integration.js.map +1 -1
- package/dist/src/utils/research-questions.d.ts.map +1 -1
- package/dist/src/utils/research-questions.js +40 -14
- package/dist/src/utils/research-questions.js.map +1 -1
- package/dist/src/utils/ripgrep-wrapper.d.ts +84 -0
- package/dist/src/utils/ripgrep-wrapper.d.ts.map +1 -0
- package/dist/src/utils/ripgrep-wrapper.js +255 -0
- package/dist/src/utils/ripgrep-wrapper.js.map +1 -0
- package/dist/src/utils/rule-generation.d.ts.map +1 -1
- package/dist/src/utils/rule-generation.js +26 -15
- package/dist/src/utils/rule-generation.js.map +1 -1
- package/dist/src/utils/todo-file-watcher.d.ts +11 -23
- package/dist/src/utils/todo-file-watcher.d.ts.map +1 -1
- package/dist/src/utils/todo-file-watcher.js +18 -156
- package/dist/src/utils/todo-file-watcher.js.map +1 -1
- package/dist/src/utils/tree-sitter-analyzer.d.ts +185 -0
- package/dist/src/utils/tree-sitter-analyzer.d.ts.map +1 -0
- package/dist/src/utils/tree-sitter-analyzer.js +885 -0
- package/dist/src/utils/tree-sitter-analyzer.js.map +1 -0
- package/package.json +51 -10
- package/dist/src/tools/smart-score-tool.d.ts +0 -173
- package/dist/src/tools/smart-score-tool.d.ts.map +0 -1
- package/dist/src/tools/smart-score-tool.js +0 -681
- package/dist/src/tools/smart-score-tool.js.map +0 -1
- package/dist/src/tools/todo-management-tool-v2.d.ts +0 -368
- package/dist/src/tools/todo-management-tool-v2.d.ts.map +0 -1
- package/dist/src/tools/todo-management-tool-v2.js +0 -922
- package/dist/src/tools/todo-management-tool-v2.js.map +0 -1
- package/dist/src/utils/enhanced-sensitive-detector.d.ts +0 -61
- package/dist/src/utils/enhanced-sensitive-detector.d.ts.map +0 -1
- package/dist/src/utils/enhanced-sensitive-detector.js +0 -437
- package/dist/src/utils/enhanced-sensitive-detector.js.map +0 -1
- package/dist/src/utils/project-health-scoring.d.ts +0 -177
- package/dist/src/utils/project-health-scoring.d.ts.map +0 -1
- package/dist/src/utils/project-health-scoring.js +0 -370
- package/dist/src/utils/project-health-scoring.js.map +0 -1
- package/dist/src/utils/todo-json-manager.d.ts +0 -71
- package/dist/src/utils/todo-json-manager.d.ts.map +0 -1
- package/dist/src/utils/todo-json-manager.js +0 -396
- package/dist/src/utils/todo-json-manager.js.map +0 -1
- package/dist/src/utils/todo-markdown-converter.d.ts +0 -16
- package/dist/src/utils/todo-markdown-converter.d.ts.map +0 -1
- package/dist/src/utils/todo-markdown-converter.js +0 -362
- package/dist/src/utils/todo-markdown-converter.js.map +0 -1
- package/dist/src/utils/todo-migration.d.ts +0 -47
- package/dist/src/utils/todo-migration.d.ts.map +0 -1
- package/dist/src/utils/todo-migration.js +0 -261
- package/dist/src/utils/todo-migration.js.map +0 -1
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Entity Manager
|
|
3
|
+
*
|
|
4
|
+
* Core manager for the memory-centric architecture that handles storage, retrieval,
|
|
5
|
+
* relationship management, and intelligence operations for memory entities.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs/promises';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as os from 'os';
|
|
10
|
+
import crypto from 'crypto';
|
|
11
|
+
import { MemoryEntitySchema, } from '../types/memory-entities.js';
|
|
12
|
+
import { McpAdrError } from '../types/index.js';
|
|
13
|
+
import { loadConfig } from './config.js';
|
|
14
|
+
import { EnhancedLogger } from './enhanced-logging.js';
|
|
15
|
+
export class MemoryEntityManager {
|
|
16
|
+
fsOverride;
|
|
17
|
+
memoryDir;
|
|
18
|
+
entitiesFile;
|
|
19
|
+
relationshipsFile;
|
|
20
|
+
intelligenceFile;
|
|
21
|
+
evolutionLogFile;
|
|
22
|
+
config;
|
|
23
|
+
logger;
|
|
24
|
+
isTestMode;
|
|
25
|
+
// In-memory caches for performance
|
|
26
|
+
entitiesCache = new Map();
|
|
27
|
+
relationshipsCache = new Map();
|
|
28
|
+
intelligence = null;
|
|
29
|
+
lastSnapshotTime = 0;
|
|
30
|
+
constructor(config, testMode = false, fsOverride) {
|
|
31
|
+
this.fsOverride = fsOverride;
|
|
32
|
+
this.isTestMode = testMode;
|
|
33
|
+
const projectConfig = loadConfig();
|
|
34
|
+
this.logger = new EnhancedLogger();
|
|
35
|
+
// Use OS temp directory for memory storage
|
|
36
|
+
const projectName = path.basename(projectConfig.projectPath);
|
|
37
|
+
this.memoryDir = path.join(os.tmpdir(), projectName, 'memory');
|
|
38
|
+
this.entitiesFile = path.join(this.memoryDir, 'entities.json');
|
|
39
|
+
this.relationshipsFile = path.join(this.memoryDir, 'relationships.json');
|
|
40
|
+
this.intelligenceFile = path.join(this.memoryDir, 'intelligence.json');
|
|
41
|
+
this.evolutionLogFile = path.join(this.memoryDir, 'evolution-log.json');
|
|
42
|
+
this.config = {
|
|
43
|
+
storageType: 'file',
|
|
44
|
+
snapshotFrequency: testMode ? 0 : 30, // No automatic persistence in test mode
|
|
45
|
+
compressionEnabled: true,
|
|
46
|
+
backupRetention: 30, // 30 days
|
|
47
|
+
syncEnabled: !testMode, // Disable sync in test mode
|
|
48
|
+
conflictResolution: 'merge',
|
|
49
|
+
...config,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Initialize the memory system
|
|
54
|
+
*/
|
|
55
|
+
async initialize() {
|
|
56
|
+
try {
|
|
57
|
+
await this.ensureMemoryDirectory();
|
|
58
|
+
await this.loadFromPersistence();
|
|
59
|
+
await this.initializeIntelligence();
|
|
60
|
+
this.logger.info('Memory Entity Manager initialized successfully', 'MemoryEntityManager', {
|
|
61
|
+
entityCount: this.entitiesCache.size,
|
|
62
|
+
relationshipCount: this.relationshipsCache.size,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw new McpAdrError(`Failed to initialize memory system: ${error instanceof Error ? error.message : String(error)}`, 'MEMORY_INIT_ERROR');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create or update a memory entity
|
|
71
|
+
*/
|
|
72
|
+
async upsertEntity(entity) {
|
|
73
|
+
try {
|
|
74
|
+
const now = new Date().toISOString();
|
|
75
|
+
const id = entity.id || crypto.randomUUID();
|
|
76
|
+
// Get existing entity if updating
|
|
77
|
+
const existing = entity.id ? this.entitiesCache.get(entity.id) : null;
|
|
78
|
+
// Create full entity with defaults
|
|
79
|
+
const fullEntity = {
|
|
80
|
+
id,
|
|
81
|
+
created: existing?.created || now,
|
|
82
|
+
lastModified: now,
|
|
83
|
+
version: (existing?.version || 0) + 1,
|
|
84
|
+
confidence: entity.confidence || 0.85,
|
|
85
|
+
relevance: entity.relevance || 0.7,
|
|
86
|
+
tags: entity.tags || [],
|
|
87
|
+
relationships: entity.relationships || existing?.relationships || [],
|
|
88
|
+
context: {
|
|
89
|
+
projectPhase: entity.context?.projectPhase,
|
|
90
|
+
businessDomain: entity.context?.businessDomain,
|
|
91
|
+
technicalStack: entity.context?.technicalStack || [],
|
|
92
|
+
environmentalFactors: entity.context?.environmentalFactors || [],
|
|
93
|
+
stakeholders: entity.context?.stakeholders || [],
|
|
94
|
+
},
|
|
95
|
+
accessPattern: {
|
|
96
|
+
lastAccessed: now,
|
|
97
|
+
accessCount: (existing?.accessPattern?.accessCount || 0) + 1,
|
|
98
|
+
accessContext: entity.accessPattern?.accessContext || [],
|
|
99
|
+
},
|
|
100
|
+
evolution: {
|
|
101
|
+
origin: entity.evolution?.origin || (existing ? existing.evolution.origin : 'created'),
|
|
102
|
+
transformations: [
|
|
103
|
+
...(existing?.evolution?.transformations || []),
|
|
104
|
+
{
|
|
105
|
+
timestamp: now,
|
|
106
|
+
type: existing ? 'updated' : 'created',
|
|
107
|
+
description: existing
|
|
108
|
+
? `Updated ${entity.type} entity`
|
|
109
|
+
: `Created ${entity.type} entity`,
|
|
110
|
+
agent: 'MemoryEntityManager',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
validation: {
|
|
115
|
+
isVerified: entity.validation?.isVerified || false,
|
|
116
|
+
verificationMethod: entity.validation?.verificationMethod,
|
|
117
|
+
verificationTimestamp: entity.validation?.verificationTimestamp,
|
|
118
|
+
conflictResolution: entity.validation?.conflictResolution,
|
|
119
|
+
},
|
|
120
|
+
...entity,
|
|
121
|
+
};
|
|
122
|
+
// Validate the entity
|
|
123
|
+
try {
|
|
124
|
+
MemoryEntitySchema.parse(fullEntity);
|
|
125
|
+
}
|
|
126
|
+
catch (validationError) {
|
|
127
|
+
throw new McpAdrError(`Entity validation failed: ${validationError instanceof Error ? validationError.message : String(validationError)}`, 'ENTITY_VALIDATION_ERROR');
|
|
128
|
+
}
|
|
129
|
+
// Store in cache
|
|
130
|
+
this.entitiesCache.set(id, fullEntity);
|
|
131
|
+
// Log evolution event
|
|
132
|
+
await this.logEvolutionEvent({
|
|
133
|
+
id: crypto.randomUUID(),
|
|
134
|
+
entityId: id,
|
|
135
|
+
timestamp: now,
|
|
136
|
+
eventType: existing ? 'modified' : 'created',
|
|
137
|
+
agent: 'MemoryEntityManager',
|
|
138
|
+
changes: existing ? this.calculateChanges(existing, fullEntity) : {},
|
|
139
|
+
});
|
|
140
|
+
// Update intelligence
|
|
141
|
+
await this.updateIntelligence('entity_updated', {
|
|
142
|
+
entityId: id,
|
|
143
|
+
entityType: fullEntity.type,
|
|
144
|
+
});
|
|
145
|
+
// Persist if needed
|
|
146
|
+
await this.maybePersist();
|
|
147
|
+
this.logger.debug(`${existing ? 'Updated' : 'Created'} entity`, 'MemoryEntityManager', {
|
|
148
|
+
entityId: id,
|
|
149
|
+
type: fullEntity.type,
|
|
150
|
+
title: fullEntity.title.substring(0, 50),
|
|
151
|
+
});
|
|
152
|
+
return fullEntity;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw new McpAdrError(`Failed to upsert entity: ${error instanceof Error ? error.message : String(error)}`, 'ENTITY_UPSERT_ERROR');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get an entity by ID
|
|
160
|
+
*/
|
|
161
|
+
async getEntity(id) {
|
|
162
|
+
const entity = this.entitiesCache.get(id);
|
|
163
|
+
if (entity) {
|
|
164
|
+
// Update access pattern
|
|
165
|
+
entity.accessPattern.lastAccessed = new Date().toISOString();
|
|
166
|
+
entity.accessPattern.accessCount += 1;
|
|
167
|
+
await this.logEvolutionEvent({
|
|
168
|
+
id: crypto.randomUUID(),
|
|
169
|
+
entityId: id,
|
|
170
|
+
timestamp: new Date().toISOString(),
|
|
171
|
+
eventType: 'accessed',
|
|
172
|
+
agent: 'MemoryEntityManager',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return entity || null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Delete an entity
|
|
179
|
+
*/
|
|
180
|
+
async deleteEntity(id) {
|
|
181
|
+
const entity = this.entitiesCache.get(id);
|
|
182
|
+
if (!entity)
|
|
183
|
+
return false;
|
|
184
|
+
// Remove entity
|
|
185
|
+
this.entitiesCache.delete(id);
|
|
186
|
+
// Remove all relationships involving this entity
|
|
187
|
+
const relationshipsToRemove = Array.from(this.relationshipsCache.values()).filter(rel => rel.sourceId === id || rel.targetId === id);
|
|
188
|
+
relationshipsToRemove.forEach(rel => this.relationshipsCache.delete(rel.id));
|
|
189
|
+
// Log evolution event
|
|
190
|
+
await this.logEvolutionEvent({
|
|
191
|
+
id: crypto.randomUUID(),
|
|
192
|
+
entityId: id,
|
|
193
|
+
timestamp: new Date().toISOString(),
|
|
194
|
+
eventType: 'deprecated',
|
|
195
|
+
agent: 'MemoryEntityManager',
|
|
196
|
+
context: 'Entity deleted by user request',
|
|
197
|
+
});
|
|
198
|
+
await this.maybePersist();
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Create or update a relationship between entities
|
|
203
|
+
*/
|
|
204
|
+
async upsertRelationship(relationship) {
|
|
205
|
+
try {
|
|
206
|
+
const now = new Date().toISOString();
|
|
207
|
+
const id = relationship.id || crypto.randomUUID();
|
|
208
|
+
// Validate that both entities exist
|
|
209
|
+
const sourceEntity = this.entitiesCache.get(relationship.sourceId);
|
|
210
|
+
const targetEntity = this.entitiesCache.get(relationship.targetId);
|
|
211
|
+
if (!sourceEntity || !targetEntity) {
|
|
212
|
+
throw new McpAdrError('Source or target entity not found', 'RELATIONSHIP_INVALID_ENTITIES');
|
|
213
|
+
}
|
|
214
|
+
const fullRelationship = {
|
|
215
|
+
id,
|
|
216
|
+
sourceId: relationship.sourceId,
|
|
217
|
+
targetId: relationship.targetId,
|
|
218
|
+
type: relationship.type,
|
|
219
|
+
strength: relationship.strength || 0.7,
|
|
220
|
+
context: relationship.context || '',
|
|
221
|
+
evidence: relationship.evidence || [],
|
|
222
|
+
created: relationship.created || now,
|
|
223
|
+
lastValidated: relationship.lastValidated || now,
|
|
224
|
+
confidence: relationship.confidence || 0.85,
|
|
225
|
+
};
|
|
226
|
+
// Store in cache
|
|
227
|
+
this.relationshipsCache.set(id, fullRelationship);
|
|
228
|
+
// Update entity relationships for both source and target
|
|
229
|
+
this.updateEntityRelationships(sourceEntity, fullRelationship);
|
|
230
|
+
this.updateEntityRelationships(targetEntity, fullRelationship);
|
|
231
|
+
// Log evolution events for both entities
|
|
232
|
+
await Promise.all([
|
|
233
|
+
this.logEvolutionEvent({
|
|
234
|
+
id: crypto.randomUUID(),
|
|
235
|
+
entityId: relationship.sourceId,
|
|
236
|
+
timestamp: now,
|
|
237
|
+
eventType: 'related',
|
|
238
|
+
agent: 'MemoryEntityManager',
|
|
239
|
+
context: `Relationship ${relationship.type} to ${targetEntity.title}`,
|
|
240
|
+
}),
|
|
241
|
+
this.logEvolutionEvent({
|
|
242
|
+
id: crypto.randomUUID(),
|
|
243
|
+
entityId: relationship.targetId,
|
|
244
|
+
timestamp: now,
|
|
245
|
+
eventType: 'related',
|
|
246
|
+
agent: 'MemoryEntityManager',
|
|
247
|
+
context: `Relationship ${relationship.type} from ${sourceEntity.title}`,
|
|
248
|
+
}),
|
|
249
|
+
]);
|
|
250
|
+
await this.maybePersist();
|
|
251
|
+
return fullRelationship;
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
throw new McpAdrError(`Failed to create relationship: ${error instanceof Error ? error.message : String(error)}`, 'RELATIONSHIP_CREATE_ERROR');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Query entities with advanced filtering
|
|
259
|
+
*/
|
|
260
|
+
async queryEntities(query) {
|
|
261
|
+
const startTime = Date.now();
|
|
262
|
+
try {
|
|
263
|
+
let entities = Array.from(this.entitiesCache.values());
|
|
264
|
+
const relationships = Array.from(this.relationshipsCache.values());
|
|
265
|
+
// Apply filters
|
|
266
|
+
if (query.entityTypes?.length) {
|
|
267
|
+
entities = entities.filter(e => query.entityTypes.includes(e.type));
|
|
268
|
+
}
|
|
269
|
+
if (query.tags?.length) {
|
|
270
|
+
entities = entities.filter(e => query.tags.some(tag => e.tags.includes(tag)));
|
|
271
|
+
}
|
|
272
|
+
if (query.textQuery) {
|
|
273
|
+
const searchTerm = query.textQuery.toLowerCase();
|
|
274
|
+
entities = entities.filter(e => e.title.toLowerCase().includes(searchTerm) ||
|
|
275
|
+
e.description.toLowerCase().includes(searchTerm) ||
|
|
276
|
+
e.tags.some(tag => tag.toLowerCase().includes(searchTerm)));
|
|
277
|
+
}
|
|
278
|
+
if (query.confidenceThreshold !== undefined) {
|
|
279
|
+
entities = entities.filter(e => e.confidence >= query.confidenceThreshold);
|
|
280
|
+
}
|
|
281
|
+
if (query.relevanceThreshold !== undefined) {
|
|
282
|
+
entities = entities.filter(e => e.relevance >= query.relevanceThreshold);
|
|
283
|
+
}
|
|
284
|
+
if (query.timeRange) {
|
|
285
|
+
const fromTime = new Date(query.timeRange.from).getTime();
|
|
286
|
+
const toTime = new Date(query.timeRange.to).getTime();
|
|
287
|
+
entities = entities.filter(e => {
|
|
288
|
+
const entityTime = new Date(e.lastModified).getTime();
|
|
289
|
+
return entityTime >= fromTime && entityTime <= toTime;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Apply context filters
|
|
293
|
+
if (query.contextFilters) {
|
|
294
|
+
if (query.contextFilters.projectPhase) {
|
|
295
|
+
entities = entities.filter(e => e.context.projectPhase === query.contextFilters.projectPhase);
|
|
296
|
+
}
|
|
297
|
+
if (query.contextFilters.businessDomain) {
|
|
298
|
+
entities = entities.filter(e => e.context.businessDomain === query.contextFilters.businessDomain);
|
|
299
|
+
}
|
|
300
|
+
if (query.contextFilters.technicalStack?.length) {
|
|
301
|
+
entities = entities.filter(e => query.contextFilters.technicalStack.some(tech => e.context.technicalStack.includes(tech)));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Sort entities
|
|
305
|
+
const sortBy = query.sortBy || 'relevance';
|
|
306
|
+
entities.sort((a, b) => {
|
|
307
|
+
switch (sortBy) {
|
|
308
|
+
case 'confidence':
|
|
309
|
+
return b.confidence - a.confidence;
|
|
310
|
+
case 'lastModified':
|
|
311
|
+
return new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime();
|
|
312
|
+
case 'created':
|
|
313
|
+
return new Date(b.created).getTime() - new Date(a.created).getTime();
|
|
314
|
+
case 'accessCount':
|
|
315
|
+
return b.accessPattern.accessCount - a.accessPattern.accessCount;
|
|
316
|
+
case 'relevance':
|
|
317
|
+
default:
|
|
318
|
+
return b.relevance - a.relevance;
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
const totalCount = entities.length;
|
|
322
|
+
// Generate aggregations on ALL matching entities before applying limit
|
|
323
|
+
const aggregations = {
|
|
324
|
+
byType: {},
|
|
325
|
+
byTag: {},
|
|
326
|
+
byConfidence: {},
|
|
327
|
+
};
|
|
328
|
+
entities.forEach(entity => {
|
|
329
|
+
// By type
|
|
330
|
+
aggregations.byType[entity.type] = (aggregations.byType[entity.type] || 0) + 1;
|
|
331
|
+
// By tags
|
|
332
|
+
entity.tags.forEach(tag => {
|
|
333
|
+
aggregations.byTag[tag] = (aggregations.byTag[tag] || 0) + 1;
|
|
334
|
+
});
|
|
335
|
+
// By confidence range
|
|
336
|
+
const confidenceRange = Math.floor(entity.confidence * 10) / 10;
|
|
337
|
+
aggregations.byConfidence[`${confidenceRange}-${confidenceRange + 0.1}`] =
|
|
338
|
+
(aggregations.byConfidence[`${confidenceRange}-${confidenceRange + 0.1}`] || 0) + 1;
|
|
339
|
+
});
|
|
340
|
+
// Apply limit AFTER generating aggregations
|
|
341
|
+
if (query.limit && query.limit > 0) {
|
|
342
|
+
entities = entities.slice(0, query.limit);
|
|
343
|
+
}
|
|
344
|
+
// Include related entities if requested
|
|
345
|
+
let resultRelationships = relationships;
|
|
346
|
+
if (query.includeRelated) {
|
|
347
|
+
const entityIds = new Set(entities.map(e => e.id));
|
|
348
|
+
resultRelationships = relationships.filter(r => entityIds.has(r.sourceId) || entityIds.has(r.targetId));
|
|
349
|
+
}
|
|
350
|
+
const queryTime = Date.now() - startTime;
|
|
351
|
+
this.logger.debug('Entity query executed', 'MemoryEntityManager', {
|
|
352
|
+
queryTime,
|
|
353
|
+
totalCount,
|
|
354
|
+
returnedCount: entities.length,
|
|
355
|
+
filters: Object.keys(query).filter(key => query[key] !== undefined),
|
|
356
|
+
});
|
|
357
|
+
return {
|
|
358
|
+
entities,
|
|
359
|
+
relationships: resultRelationships,
|
|
360
|
+
totalCount,
|
|
361
|
+
queryTime,
|
|
362
|
+
aggregations,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
throw new McpAdrError(`Failed to query entities: ${error instanceof Error ? error.message : String(error)}`, 'QUERY_ERROR');
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Find related entities through relationships
|
|
371
|
+
*/
|
|
372
|
+
async findRelatedEntities(entityId, maxDepth = 2, relationshipTypes) {
|
|
373
|
+
const visited = new Set();
|
|
374
|
+
const paths = [];
|
|
375
|
+
const relatedEntities = new Map();
|
|
376
|
+
const traverse = (currentId, currentPath, currentRelationships, depth) => {
|
|
377
|
+
if (depth > maxDepth || visited.has(currentId))
|
|
378
|
+
return;
|
|
379
|
+
visited.add(currentId);
|
|
380
|
+
const currentEntity = this.entitiesCache.get(currentId);
|
|
381
|
+
if (!currentEntity)
|
|
382
|
+
return;
|
|
383
|
+
if (depth > 0) {
|
|
384
|
+
relatedEntities.set(currentId, currentEntity);
|
|
385
|
+
paths.push({
|
|
386
|
+
path: [...currentPath, currentId],
|
|
387
|
+
relationships: [...currentRelationships],
|
|
388
|
+
depth,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
// Find both outgoing and incoming relationships
|
|
392
|
+
const allRels = Array.from(this.relationshipsCache.values()).filter(rel => {
|
|
393
|
+
const isOutgoing = rel.sourceId === currentId;
|
|
394
|
+
const isIncoming = rel.targetId === currentId;
|
|
395
|
+
if (!isOutgoing && !isIncoming)
|
|
396
|
+
return false;
|
|
397
|
+
if (relationshipTypes?.length && !relationshipTypes.includes(rel.type))
|
|
398
|
+
return false;
|
|
399
|
+
return true;
|
|
400
|
+
});
|
|
401
|
+
for (const rel of allRels) {
|
|
402
|
+
const targetId = rel.sourceId === currentId ? rel.targetId : rel.sourceId;
|
|
403
|
+
if (!visited.has(targetId)) {
|
|
404
|
+
traverse(targetId, [...currentPath, currentId], [...currentRelationships, rel], depth + 1);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
traverse(entityId, [], [], 0);
|
|
409
|
+
return {
|
|
410
|
+
entities: Array.from(relatedEntities.values()),
|
|
411
|
+
relationshipPaths: paths,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get memory intelligence data
|
|
416
|
+
*/
|
|
417
|
+
async getIntelligence() {
|
|
418
|
+
if (!this.intelligence) {
|
|
419
|
+
await this.initializeIntelligence();
|
|
420
|
+
}
|
|
421
|
+
return this.intelligence;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Create a memory snapshot
|
|
425
|
+
*/
|
|
426
|
+
async createSnapshot() {
|
|
427
|
+
const now = new Date().toISOString();
|
|
428
|
+
const intelligence = await this.getIntelligence();
|
|
429
|
+
const entities = Array.from(this.entitiesCache.values());
|
|
430
|
+
const relationships = Array.from(this.relationshipsCache.values());
|
|
431
|
+
const totalConfidence = entities.reduce((sum, e) => sum + e.confidence, 0);
|
|
432
|
+
const averageConfidence = entities.length > 0 ? totalConfidence / entities.length : 0;
|
|
433
|
+
return {
|
|
434
|
+
id: crypto.randomUUID(),
|
|
435
|
+
timestamp: now,
|
|
436
|
+
version: '1.0.0',
|
|
437
|
+
entities,
|
|
438
|
+
relationships,
|
|
439
|
+
intelligence,
|
|
440
|
+
metadata: {
|
|
441
|
+
totalEntities: entities.length,
|
|
442
|
+
totalRelationships: relationships.length,
|
|
443
|
+
averageConfidence,
|
|
444
|
+
lastOptimization: now,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
// Private helper methods
|
|
449
|
+
async ensureMemoryDirectory() {
|
|
450
|
+
try {
|
|
451
|
+
const fsInstance = this.fsOverride || fs;
|
|
452
|
+
await fsInstance.access(this.memoryDir);
|
|
453
|
+
}
|
|
454
|
+
catch {
|
|
455
|
+
// Directory doesn't exist, create it
|
|
456
|
+
const fsInstance = this.fsOverride || fs;
|
|
457
|
+
await fsInstance.mkdir(this.memoryDir, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async loadFromPersistence() {
|
|
461
|
+
// Skip loading from persistence in test mode to avoid conflicts
|
|
462
|
+
if (this.isTestMode) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
// Load entities
|
|
467
|
+
try {
|
|
468
|
+
const fsInstance = this.fsOverride || fs;
|
|
469
|
+
const entitiesData = await fsInstance.readFile(this.entitiesFile, 'utf-8');
|
|
470
|
+
const entities = JSON.parse(entitiesData);
|
|
471
|
+
entities.forEach(entity => {
|
|
472
|
+
this.entitiesCache.set(entity.id, entity);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
// No entities file exists yet
|
|
477
|
+
}
|
|
478
|
+
// Load relationships
|
|
479
|
+
try {
|
|
480
|
+
const fsInstance = this.fsOverride || fs;
|
|
481
|
+
const relationshipsData = await fsInstance.readFile(this.relationshipsFile, 'utf-8');
|
|
482
|
+
const relationships = JSON.parse(relationshipsData);
|
|
483
|
+
relationships.forEach(relationship => {
|
|
484
|
+
this.relationshipsCache.set(relationship.id, relationship);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
// No relationships file exists yet
|
|
489
|
+
}
|
|
490
|
+
// Load intelligence
|
|
491
|
+
try {
|
|
492
|
+
const fsInstance = this.fsOverride || fs;
|
|
493
|
+
const intelligenceData = await fsInstance.readFile(this.intelligenceFile, 'utf-8');
|
|
494
|
+
this.intelligence = JSON.parse(intelligenceData);
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Will initialize default intelligence
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
this.logger.warn('Failed to load some memory data from persistence', 'MemoryEntityManager', {
|
|
502
|
+
error,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async maybePersist() {
|
|
507
|
+
// In test mode, persist when there are enough changes for testing purposes
|
|
508
|
+
if (this.isTestMode) {
|
|
509
|
+
// Check if we have enough entities to test persistence (2+ entities)
|
|
510
|
+
const now = Date.now();
|
|
511
|
+
const timeSinceLastSnapshot = now - this.lastSnapshotTime;
|
|
512
|
+
// In test mode, persist after 30 minutes of simulated time OR when we have 2+ entities
|
|
513
|
+
if (this.entitiesCache.size >= 2 && timeSinceLastSnapshot >= 30 * 60 * 1000) {
|
|
514
|
+
await this.persistToStorage();
|
|
515
|
+
this.lastSnapshotTime = now;
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// Skip auto-persistence if disabled
|
|
520
|
+
if (this.config.snapshotFrequency === 0) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const now = Date.now();
|
|
524
|
+
const timeSinceLastSnapshot = now - this.lastSnapshotTime;
|
|
525
|
+
const snapshotIntervalMs = this.config.snapshotFrequency * 60 * 1000;
|
|
526
|
+
if (timeSinceLastSnapshot >= snapshotIntervalMs) {
|
|
527
|
+
await this.persistToStorage();
|
|
528
|
+
this.lastSnapshotTime = now;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async persistToStorage() {
|
|
532
|
+
try {
|
|
533
|
+
const fsInstance = this.fsOverride || fs;
|
|
534
|
+
await this.ensureMemoryDirectory();
|
|
535
|
+
// Save entities
|
|
536
|
+
const entities = Array.from(this.entitiesCache.values());
|
|
537
|
+
await fsInstance.writeFile(this.entitiesFile, JSON.stringify(entities, null, 2));
|
|
538
|
+
// Save relationships
|
|
539
|
+
const relationships = Array.from(this.relationshipsCache.values());
|
|
540
|
+
await fsInstance.writeFile(this.relationshipsFile, JSON.stringify(relationships, null, 2));
|
|
541
|
+
// Save intelligence
|
|
542
|
+
if (this.intelligence) {
|
|
543
|
+
await fsInstance.writeFile(this.intelligenceFile, JSON.stringify(this.intelligence, null, 2));
|
|
544
|
+
}
|
|
545
|
+
this.logger.debug('Memory data persisted to storage', 'MemoryEntityManager', {
|
|
546
|
+
entityCount: entities.length,
|
|
547
|
+
relationshipCount: relationships.length,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
this.logger.error('Failed to persist memory data', 'MemoryEntityManager', error instanceof Error ? error : undefined, { error });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
async initializeIntelligence() {
|
|
555
|
+
if (!this.intelligence) {
|
|
556
|
+
this.intelligence = {
|
|
557
|
+
contextAwareness: {
|
|
558
|
+
currentContext: {},
|
|
559
|
+
contextHistory: [],
|
|
560
|
+
},
|
|
561
|
+
patternRecognition: {
|
|
562
|
+
discoveredPatterns: [],
|
|
563
|
+
patternConfidence: {},
|
|
564
|
+
emergentBehaviors: [],
|
|
565
|
+
},
|
|
566
|
+
relationshipInference: {
|
|
567
|
+
suggestedRelationships: [],
|
|
568
|
+
weakConnections: [],
|
|
569
|
+
conflictDetection: [],
|
|
570
|
+
},
|
|
571
|
+
adaptiveRecommendations: {
|
|
572
|
+
nextActions: [],
|
|
573
|
+
knowledgeGaps: [],
|
|
574
|
+
optimizationOpportunities: [],
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
// Initialize with some default knowledge gaps and recommendations
|
|
578
|
+
await this.updateRecommendations();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
async updateIntelligence(eventType, context) {
|
|
582
|
+
if (!this.intelligence)
|
|
583
|
+
await this.initializeIntelligence();
|
|
584
|
+
// Update context awareness
|
|
585
|
+
this.intelligence.contextAwareness.currentContext = {
|
|
586
|
+
...this.intelligence.contextAwareness.currentContext,
|
|
587
|
+
lastEvent: eventType,
|
|
588
|
+
lastEventTime: new Date().toISOString(),
|
|
589
|
+
...context,
|
|
590
|
+
};
|
|
591
|
+
// Basic pattern recognition - could be enhanced with ML
|
|
592
|
+
if (eventType === 'entity_updated') {
|
|
593
|
+
const pattern = `${context.entityType}_update`;
|
|
594
|
+
this.intelligence.patternRecognition.patternConfidence[pattern] =
|
|
595
|
+
(this.intelligence.patternRecognition.patternConfidence[pattern] || 0) + 1;
|
|
596
|
+
}
|
|
597
|
+
// Update recommendations based on current state
|
|
598
|
+
await this.updateRecommendations();
|
|
599
|
+
}
|
|
600
|
+
async updateRecommendations() {
|
|
601
|
+
if (!this.intelligence)
|
|
602
|
+
return;
|
|
603
|
+
const entities = Array.from(this.entitiesCache.values());
|
|
604
|
+
const relationships = Array.from(this.relationshipsCache.values());
|
|
605
|
+
// Identify knowledge gaps (entities with low confidence OR system lacking entities)
|
|
606
|
+
const knowledgeGaps = [];
|
|
607
|
+
if (entities.length === 0) {
|
|
608
|
+
knowledgeGaps.push('No architectural decisions documented in memory system');
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
// Add gaps for low confidence entities
|
|
612
|
+
const lowConfidenceGaps = entities
|
|
613
|
+
.filter(e => e.confidence < 0.5)
|
|
614
|
+
.map(e => `Low confidence in ${e.type}: ${e.title}`)
|
|
615
|
+
.slice(0, 3);
|
|
616
|
+
knowledgeGaps.push(...lowConfidenceGaps);
|
|
617
|
+
// Add gaps for missing entity types
|
|
618
|
+
if (entities.length === 1) {
|
|
619
|
+
knowledgeGaps.push('Limited diversity in architectural entities - consider documenting more types');
|
|
620
|
+
}
|
|
621
|
+
// Add gaps for entities without tags
|
|
622
|
+
const untaggedEntities = entities.filter(e => !e.tags || e.tags.length === 0);
|
|
623
|
+
if (untaggedEntities.length > 0) {
|
|
624
|
+
knowledgeGaps.push(`${untaggedEntities.length} entities lack proper categorization tags`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
// Suggest relationships for entities without many connections
|
|
628
|
+
const suggestedRelationships = [];
|
|
629
|
+
for (const entity of entities) {
|
|
630
|
+
const entityRelationships = relationships.filter(r => r.sourceId === entity.id || r.targetId === entity.id);
|
|
631
|
+
if (entityRelationships.length < 2) {
|
|
632
|
+
// Find potential relationships based on tags or type similarity
|
|
633
|
+
const similar = entities.filter(other => other.id !== entity.id &&
|
|
634
|
+
(other.type === entity.type || other.tags.some(tag => entity.tags.includes(tag))));
|
|
635
|
+
for (const similarEntity of similar.slice(0, 2)) {
|
|
636
|
+
suggestedRelationships.push({
|
|
637
|
+
sourceId: entity.id,
|
|
638
|
+
targetId: similarEntity.id,
|
|
639
|
+
type: 'relates_to',
|
|
640
|
+
confidence: 0.6,
|
|
641
|
+
reasoning: `Similar ${entity.type} entities with overlapping tags`,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
this.intelligence.adaptiveRecommendations = {
|
|
647
|
+
nextActions: [
|
|
648
|
+
{
|
|
649
|
+
action: 'Validate low-confidence entities',
|
|
650
|
+
priority: 1,
|
|
651
|
+
reasoning: 'Several entities have confidence below 50%',
|
|
652
|
+
requiredEntities: entities
|
|
653
|
+
.filter(e => e.confidence < 0.5)
|
|
654
|
+
.map(e => e.id)
|
|
655
|
+
.slice(0, 3),
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
knowledgeGaps,
|
|
659
|
+
optimizationOpportunities: [
|
|
660
|
+
'Consider consolidating similar entities',
|
|
661
|
+
'Review and strengthen weak relationships',
|
|
662
|
+
'Add more context to entities with generic descriptions',
|
|
663
|
+
],
|
|
664
|
+
};
|
|
665
|
+
this.intelligence.relationshipInference.suggestedRelationships = suggestedRelationships.slice(0, 10);
|
|
666
|
+
}
|
|
667
|
+
updateEntityRelationships(entity, relationship) {
|
|
668
|
+
// Determine the target entity ID based on which entity this is
|
|
669
|
+
const isSourceEntity = entity.id === relationship.sourceId;
|
|
670
|
+
const targetEntityId = isSourceEntity ? relationship.targetId : relationship.sourceId;
|
|
671
|
+
// Add relationship to entity's relationship list if not already present
|
|
672
|
+
const existingRelIndex = entity.relationships.findIndex(r => r.targetId === targetEntityId && r.type === relationship.type);
|
|
673
|
+
const entityRel = {
|
|
674
|
+
targetId: targetEntityId,
|
|
675
|
+
type: relationship.type,
|
|
676
|
+
strength: relationship.strength,
|
|
677
|
+
context: relationship.context,
|
|
678
|
+
created: relationship.created,
|
|
679
|
+
};
|
|
680
|
+
if (existingRelIndex >= 0) {
|
|
681
|
+
entity.relationships[existingRelIndex] = entityRel;
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
entity.relationships.push(entityRel);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
calculateChanges(oldEntity, newEntity) {
|
|
688
|
+
const changes = {};
|
|
689
|
+
// Convert to any to avoid index signature access issues
|
|
690
|
+
const oldAny = oldEntity;
|
|
691
|
+
const newAny = newEntity;
|
|
692
|
+
if (oldAny['title'] !== newAny['title'])
|
|
693
|
+
changes['title'] = { old: oldAny['title'], new: newAny['title'] };
|
|
694
|
+
if (oldAny['description'] !== newAny['description'])
|
|
695
|
+
changes['description'] = { old: oldAny['description'], new: newAny['description'] };
|
|
696
|
+
if (oldEntity['confidence'] !== newEntity['confidence'])
|
|
697
|
+
changes['confidence'] = { old: oldEntity['confidence'], new: newEntity['confidence'] };
|
|
698
|
+
if (oldEntity['relevance'] !== newEntity['relevance'])
|
|
699
|
+
changes['relevance'] = { old: oldEntity['relevance'], new: newEntity['relevance'] };
|
|
700
|
+
return changes;
|
|
701
|
+
}
|
|
702
|
+
async logEvolutionEvent(event) {
|
|
703
|
+
try {
|
|
704
|
+
let existingLog = [];
|
|
705
|
+
try {
|
|
706
|
+
const fsInstance = this.fsOverride || fs;
|
|
707
|
+
const logData = await fsInstance.readFile(this.evolutionLogFile, 'utf-8');
|
|
708
|
+
existingLog = JSON.parse(logData);
|
|
709
|
+
}
|
|
710
|
+
catch {
|
|
711
|
+
// Log file doesn't exist yet
|
|
712
|
+
}
|
|
713
|
+
existingLog.push(event);
|
|
714
|
+
// Keep only the last 1000 events to manage file size
|
|
715
|
+
if (existingLog.length > 1000) {
|
|
716
|
+
existingLog = existingLog.slice(-1000);
|
|
717
|
+
}
|
|
718
|
+
const fsInstance = this.fsOverride || fs;
|
|
719
|
+
await fsInstance.writeFile(this.evolutionLogFile, JSON.stringify(existingLog, null, 2));
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
this.logger.warn('Failed to log evolution event', 'MemoryEntityManager', { event, error });
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Clear all cached data (useful for testing)
|
|
727
|
+
*/
|
|
728
|
+
clearCache() {
|
|
729
|
+
this.entitiesCache.clear();
|
|
730
|
+
this.relationshipsCache.clear();
|
|
731
|
+
this.intelligence = null;
|
|
732
|
+
this.lastSnapshotTime = 0;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Force persistence (useful for testing)
|
|
736
|
+
*/
|
|
737
|
+
async forcePersist() {
|
|
738
|
+
return this.persistToStorage();
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Create cross-tool relationships using the MemoryRelationshipMapper
|
|
742
|
+
*/
|
|
743
|
+
async createCrossToolRelationships() {
|
|
744
|
+
const { MemoryRelationshipMapper } = await import('./memory-relationship-mapper.js');
|
|
745
|
+
const mapper = new MemoryRelationshipMapper(this);
|
|
746
|
+
this.logger.info('Starting cross-tool relationship creation', 'MemoryEntityManager');
|
|
747
|
+
const result = await mapper.createCrossToolRelationships();
|
|
748
|
+
const autoCreatedCount = result.suggestedRelationships.filter(rel => rel.confidence >= 0.85).length;
|
|
749
|
+
this.logger.info('Cross-tool relationships created', 'MemoryEntityManager', {
|
|
750
|
+
totalSuggested: result.suggestedRelationships.length,
|
|
751
|
+
autoCreated: autoCreatedCount,
|
|
752
|
+
conflicts: result.conflicts.length,
|
|
753
|
+
});
|
|
754
|
+
return {
|
|
755
|
+
...result,
|
|
756
|
+
autoCreatedCount,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Infer and suggest new relationships based on existing entity patterns
|
|
761
|
+
*/
|
|
762
|
+
async inferRelationships(entityTypes, minConfidence = 0.7) {
|
|
763
|
+
const { MemoryRelationshipMapper } = await import('./memory-relationship-mapper.js');
|
|
764
|
+
const mapper = new MemoryRelationshipMapper(this, { confidenceThreshold: minConfidence });
|
|
765
|
+
const result = await mapper.createCrossToolRelationships();
|
|
766
|
+
// Filter by entity types if specified
|
|
767
|
+
let filteredSuggestions = result.suggestedRelationships;
|
|
768
|
+
if (entityTypes && entityTypes.length > 0) {
|
|
769
|
+
const entityCache = this.entitiesCache;
|
|
770
|
+
filteredSuggestions = result.suggestedRelationships.filter(rel => {
|
|
771
|
+
const sourceEntity = entityCache.get(rel.sourceId);
|
|
772
|
+
const targetEntity = entityCache.get(rel.targetId);
|
|
773
|
+
return (sourceEntity &&
|
|
774
|
+
targetEntity &&
|
|
775
|
+
(entityTypes.includes(sourceEntity.type) || entityTypes.includes(targetEntity.type)));
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
suggestedRelationships: filteredSuggestions.filter(rel => rel.confidence >= minConfidence),
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Validate existing relationships and detect conflicts
|
|
784
|
+
*/
|
|
785
|
+
async validateRelationships() {
|
|
786
|
+
const validRelationships = [];
|
|
787
|
+
const invalidRelationships = [];
|
|
788
|
+
const suggestedActions = [];
|
|
789
|
+
for (const [relationshipId, relationship] of this.relationshipsCache) {
|
|
790
|
+
// Check if both entities still exist
|
|
791
|
+
const sourceExists = this.entitiesCache.has(relationship.sourceId);
|
|
792
|
+
const targetExists = this.entitiesCache.has(relationship.targetId);
|
|
793
|
+
if (!sourceExists || !targetExists) {
|
|
794
|
+
invalidRelationships.push({
|
|
795
|
+
relationshipId,
|
|
796
|
+
reason: `${!sourceExists ? 'Source' : 'Target'} entity no longer exists`,
|
|
797
|
+
severity: 'high',
|
|
798
|
+
});
|
|
799
|
+
suggestedActions.push({
|
|
800
|
+
action: 'remove',
|
|
801
|
+
relationshipId,
|
|
802
|
+
reason: 'Orphaned relationship - entity deleted',
|
|
803
|
+
});
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
// Check relationship age and confidence
|
|
807
|
+
const createdDate = new Date(relationship.created);
|
|
808
|
+
const now = new Date();
|
|
809
|
+
const ageInDays = (now.getTime() - createdDate.getTime()) / (1000 * 60 * 60 * 24);
|
|
810
|
+
if (ageInDays > 90 && relationship.confidence < 0.6) {
|
|
811
|
+
invalidRelationships.push({
|
|
812
|
+
relationshipId,
|
|
813
|
+
reason: 'Old relationship with low confidence',
|
|
814
|
+
severity: 'medium',
|
|
815
|
+
});
|
|
816
|
+
suggestedActions.push({
|
|
817
|
+
action: 'investigate',
|
|
818
|
+
relationshipId,
|
|
819
|
+
reason: 'Requires validation - old and low confidence',
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
else if (relationship.strength < 0.3) {
|
|
823
|
+
invalidRelationships.push({
|
|
824
|
+
relationshipId,
|
|
825
|
+
reason: 'Very low relationship strength',
|
|
826
|
+
severity: 'low',
|
|
827
|
+
});
|
|
828
|
+
suggestedActions.push({
|
|
829
|
+
action: 'update',
|
|
830
|
+
relationshipId,
|
|
831
|
+
reason: 'Consider strengthening or removing weak relationship',
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
validRelationships.push(relationshipId);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
this.logger.info('Relationship validation completed', 'MemoryEntityManager', {
|
|
839
|
+
total: this.relationshipsCache.size,
|
|
840
|
+
valid: validRelationships.length,
|
|
841
|
+
invalid: invalidRelationships.length,
|
|
842
|
+
suggestedActions: suggestedActions.length,
|
|
843
|
+
});
|
|
844
|
+
return {
|
|
845
|
+
validRelationships,
|
|
846
|
+
invalidRelationships,
|
|
847
|
+
suggestedActions,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
//# sourceMappingURL=memory-entity-manager.js.map
|