@smallironman/mcp-memory-keeper 0.12.2-fork1
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/CHANGELOG.md +542 -0
- package/LICENSE +21 -0
- package/README.md +1281 -0
- package/bin/mcp-memory-keeper +54 -0
- package/dist/__tests__/e2e/issue33-reproduce.test.js +234 -0
- package/dist/__tests__/e2e/server-e2e.test.js +341 -0
- package/dist/__tests__/helpers/database-test-helper.js +160 -0
- package/dist/__tests__/helpers/test-server.js +92 -0
- package/dist/__tests__/integration/advanced-features.test.js +614 -0
- package/dist/__tests__/integration/backward-compatibility.test.js +245 -0
- package/dist/__tests__/integration/batchOperationsE2E.test.js +396 -0
- package/dist/__tests__/integration/batchOperationsHandler.test.js +1230 -0
- package/dist/__tests__/integration/channelManagementHandler.test.js +1291 -0
- package/dist/__tests__/integration/channels.test.js +376 -0
- package/dist/__tests__/integration/checkpoint.test.js +251 -0
- package/dist/__tests__/integration/concurrent-access.test.js +190 -0
- package/dist/__tests__/integration/context-operations.test.js +243 -0
- package/dist/__tests__/integration/contextDiff.test.js +852 -0
- package/dist/__tests__/integration/contextDiffHandler.test.js +976 -0
- package/dist/__tests__/integration/contextExportHandler.test.js +510 -0
- package/dist/__tests__/integration/contextGetPaginationDefaults.test.js +298 -0
- package/dist/__tests__/integration/contextReassignChannelHandler.test.js +908 -0
- package/dist/__tests__/integration/contextRelationshipsHandler.test.js +1151 -0
- package/dist/__tests__/integration/contextSearch.test.js +1054 -0
- package/dist/__tests__/integration/contextSearchHandler.test.js +552 -0
- package/dist/__tests__/integration/contextWatchActual.test.js +165 -0
- package/dist/__tests__/integration/contextWatchHandler.test.js +1500 -0
- package/dist/__tests__/integration/database-initialization.test.js +134 -0
- package/dist/__tests__/integration/enhanced-context-operations.test.js +1082 -0
- package/dist/__tests__/integration/enhancedContextGetHandler.test.js +915 -0
- package/dist/__tests__/integration/enhancedContextTimelineHandler.test.js +716 -0
- package/dist/__tests__/integration/error-cases.test.js +411 -0
- package/dist/__tests__/integration/export-import.test.js +367 -0
- package/dist/__tests__/integration/feature-flags.test.js +542 -0
- package/dist/__tests__/integration/file-operations.test.js +264 -0
- package/dist/__tests__/integration/filterBySessionId.test.js +251 -0
- package/dist/__tests__/integration/git-integration.test.js +241 -0
- package/dist/__tests__/integration/index-tools.test.js +496 -0
- package/dist/__tests__/integration/issue11-actual-bug-demo.test.js +304 -0
- package/dist/__tests__/integration/issue11-search-filters-bug.test.js +561 -0
- package/dist/__tests__/integration/issue12-checkpoint-restore-behavior.test.js +621 -0
- package/dist/__tests__/integration/issue13-key-validation.test.js +433 -0
- package/dist/__tests__/integration/issue24-final-fix.test.js +241 -0
- package/dist/__tests__/integration/issue24-fix-validation.test.js +158 -0
- package/dist/__tests__/integration/issue24-reproduce.test.js +225 -0
- package/dist/__tests__/integration/issue24-token-limit.test.js +199 -0
- package/dist/__tests__/integration/issue33-array-items-schema.test.js +165 -0
- package/dist/__tests__/integration/knowledge-graph.test.js +338 -0
- package/dist/__tests__/integration/migrations.test.js +528 -0
- package/dist/__tests__/integration/multi-agent.test.js +546 -0
- package/dist/__tests__/integration/pagination-critical-fix.test.js +296 -0
- package/dist/__tests__/integration/paginationDefaultsHandler.test.js +600 -0
- package/dist/__tests__/integration/project-directory.test.js +291 -0
- package/dist/__tests__/integration/resource-cleanup.test.js +149 -0
- package/dist/__tests__/integration/retention.test.js +513 -0
- package/dist/__tests__/integration/search.test.js +333 -0
- package/dist/__tests__/integration/semantic-search.test.js +266 -0
- package/dist/__tests__/integration/server-initialization.test.js +305 -0
- package/dist/__tests__/integration/session-management.test.js +219 -0
- package/dist/__tests__/integration/simplified-sharing.test.js +346 -0
- package/dist/__tests__/integration/smart-compaction.test.js +230 -0
- package/dist/__tests__/integration/summarization.test.js +308 -0
- package/dist/__tests__/integration/tokenLimitEnforcement.test.js +134 -0
- package/dist/__tests__/integration/tool-profiles-integration.test.js +150 -0
- package/dist/__tests__/integration/watcher-migration-validation.test.js +544 -0
- package/dist/__tests__/security/input-validation.test.js +115 -0
- package/dist/__tests__/utils/agents.test.js +473 -0
- package/dist/__tests__/utils/database.test.js +177 -0
- package/dist/__tests__/utils/git.test.js +122 -0
- package/dist/__tests__/utils/knowledge-graph.test.js +297 -0
- package/dist/__tests__/utils/migrationHealthCheck.test.js +302 -0
- package/dist/__tests__/utils/project-directory-messages.test.js +192 -0
- package/dist/__tests__/utils/timezone-safe-dates.js +119 -0
- package/dist/__tests__/utils/token-limits.test.js +225 -0
- package/dist/__tests__/utils/tool-profiles.test.js +374 -0
- package/dist/__tests__/utils/validation.test.js +200 -0
- package/dist/__tests__/utils/vector-store.test.js +231 -0
- package/dist/handlers/contextWatchHandlers.js +206 -0
- package/dist/index.js +4425 -0
- package/dist/migrations/003_add_channels.js +174 -0
- package/dist/migrations/004_add_context_watch.js +151 -0
- package/dist/migrations/005_add_context_watch.js +98 -0
- package/dist/migrations/simplify-sharing.js +117 -0
- package/dist/repositories/BaseRepository.js +30 -0
- package/dist/repositories/CheckpointRepository.js +140 -0
- package/dist/repositories/ContextRepository.js +2017 -0
- package/dist/repositories/FileRepository.js +104 -0
- package/dist/repositories/RepositoryManager.js +62 -0
- package/dist/repositories/SessionRepository.js +66 -0
- package/dist/repositories/WatcherRepository.js +252 -0
- package/dist/repositories/index.js +15 -0
- package/dist/test-helpers/database-helper.js +128 -0
- package/dist/types/entities.js +3 -0
- package/dist/utils/agents.js +791 -0
- package/dist/utils/channels.js +150 -0
- package/dist/utils/database.js +780 -0
- package/dist/utils/feature-flags.js +476 -0
- package/dist/utils/git.js +145 -0
- package/dist/utils/knowledge-graph.js +264 -0
- package/dist/utils/migrationHealthCheck.js +373 -0
- package/dist/utils/migrations.js +452 -0
- package/dist/utils/retention.js +460 -0
- package/dist/utils/timestamps.js +112 -0
- package/dist/utils/token-limits.js +350 -0
- package/dist/utils/tool-profiles.js +242 -0
- package/dist/utils/validation.js +296 -0
- package/dist/utils/vector-store.js +247 -0
- package/examples/config.json +31 -0
- package/examples/project-directory-setup.md +114 -0
- package/package.json +85 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const globals_1 = require("@jest/globals");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Issue #33: full tool profile exposes a schema that breaks some OpenAI-compatible providers
|
|
41
|
+
*
|
|
42
|
+
* Some providers reject tool schemas where an array property lacks an `items` declaration.
|
|
43
|
+
* Per JSON Schema spec, `items` is required for `type: 'array'` to fully describe the schema.
|
|
44
|
+
*
|
|
45
|
+
* This test scans src/index.ts and verifies that every property with `type: 'array'`
|
|
46
|
+
* has an `items` declaration within the same schema block.
|
|
47
|
+
*
|
|
48
|
+
* @see https://github.com/mkreyman/mcp-memory-keeper/issues/33
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* Scan the source for tool definitions and find array properties missing `items`.
|
|
52
|
+
* Skips block comments to avoid false positives from commented-out schemas.
|
|
53
|
+
* Line numbers refer to the original source file for accurate debugging.
|
|
54
|
+
*/
|
|
55
|
+
function findArrayPropertiesMissingItems(src) {
|
|
56
|
+
const violations = [];
|
|
57
|
+
const lines = src.split('\n');
|
|
58
|
+
let currentTool = '';
|
|
59
|
+
let inBlockComment = false;
|
|
60
|
+
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
const line = lines[i];
|
|
62
|
+
// Track block comment state
|
|
63
|
+
if (inBlockComment) {
|
|
64
|
+
if (line.includes('*/')) {
|
|
65
|
+
inBlockComment = false;
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (line.includes('/*')) {
|
|
70
|
+
inBlockComment = true;
|
|
71
|
+
if (line.includes('*/')) {
|
|
72
|
+
inBlockComment = false;
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Track which tool we're inside
|
|
77
|
+
const toolMatch = line.match(/name:\s*'(context_[a-z_]+)'/);
|
|
78
|
+
if (toolMatch) {
|
|
79
|
+
currentTool = toolMatch[1];
|
|
80
|
+
}
|
|
81
|
+
// Find array type declarations
|
|
82
|
+
if (!line.match(/type:\s*'array'/))
|
|
83
|
+
continue;
|
|
84
|
+
if (!currentTool)
|
|
85
|
+
continue;
|
|
86
|
+
// Find the property name (check current line first for single-line declarations)
|
|
87
|
+
let propertyName = '(unknown)';
|
|
88
|
+
const currentLinePropMatch = line.match(/(\w+)\s*:\s*\{/);
|
|
89
|
+
if (currentLinePropMatch) {
|
|
90
|
+
propertyName = currentLinePropMatch[1];
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
for (let j = i - 1; j >= Math.max(0, i - 5); j--) {
|
|
94
|
+
const propMatch = lines[j].match(/(\w+)\s*:\s*\{/);
|
|
95
|
+
if (propMatch) {
|
|
96
|
+
propertyName = propMatch[1];
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Check current line first (handles single-line array declarations)
|
|
102
|
+
let foundItems = line.includes('items');
|
|
103
|
+
let depth = 0;
|
|
104
|
+
for (let j = i + 1; !foundItems && j < Math.min(lines.length, i + 50); j++) {
|
|
105
|
+
const fwdLine = lines[j];
|
|
106
|
+
for (const ch of fwdLine) {
|
|
107
|
+
if (ch === '{')
|
|
108
|
+
depth++;
|
|
109
|
+
if (ch === '}')
|
|
110
|
+
depth--;
|
|
111
|
+
}
|
|
112
|
+
if (fwdLine.match(/^\s*items\s*:/)) {
|
|
113
|
+
foundItems = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (depth < 0)
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
if (!foundItems) {
|
|
120
|
+
violations.push({
|
|
121
|
+
tool: currentTool,
|
|
122
|
+
property: propertyName,
|
|
123
|
+
line: i + 1,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return violations;
|
|
128
|
+
}
|
|
129
|
+
(0, globals_1.describe)('Issue #33: Array properties must declare items', () => {
|
|
130
|
+
const indexPath = path.join(__dirname, '..', '..', 'index.ts');
|
|
131
|
+
const src = fs.readFileSync(indexPath, 'utf-8');
|
|
132
|
+
(0, globals_1.it)('should find tool definitions in source', () => {
|
|
133
|
+
const toolNames = src.match(/name:\s*'context_[a-z_]+'/g);
|
|
134
|
+
(0, globals_1.expect)(toolNames).not.toBeNull();
|
|
135
|
+
(0, globals_1.expect)(toolNames.length).toBeGreaterThan(0);
|
|
136
|
+
});
|
|
137
|
+
(0, globals_1.it)('every array property in every tool schema must have an items declaration', () => {
|
|
138
|
+
const violations = findArrayPropertiesMissingItems(src);
|
|
139
|
+
(0, globals_1.expect)(violations).toHaveLength(0);
|
|
140
|
+
});
|
|
141
|
+
(0, globals_1.it)('context_delegate.input.insights specifically must have items', () => {
|
|
142
|
+
const lines = src.split('\n');
|
|
143
|
+
let inDelegate = false;
|
|
144
|
+
let insightsLine = -1;
|
|
145
|
+
for (let i = 0; i < lines.length; i++) {
|
|
146
|
+
if (lines[i].match(/name:\s*'context_delegate'/)) {
|
|
147
|
+
inDelegate = true;
|
|
148
|
+
}
|
|
149
|
+
if (inDelegate &&
|
|
150
|
+
i > 0 &&
|
|
151
|
+
lines[i].match(/name:\s*'context_/) &&
|
|
152
|
+
!lines[i].match(/context_delegate/)) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (inDelegate && lines[i].match(/insights\s*:\s*\{/)) {
|
|
156
|
+
insightsLine = i;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
(0, globals_1.expect)(insightsLine).toBeGreaterThan(-1);
|
|
161
|
+
const insightsBlock = lines.slice(insightsLine, insightsLine + 5).join('\n');
|
|
162
|
+
(0, globals_1.expect)(insightsBlock).toContain("type: 'array'");
|
|
163
|
+
(0, globals_1.expect)(insightsBlock).toMatch(/items\s*:/);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const database_1 = require("../../utils/database");
|
|
37
|
+
const knowledge_graph_1 = require("../../utils/knowledge-graph");
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const uuid_1 = require("uuid");
|
|
42
|
+
describe('Knowledge Graph Integration Tests', () => {
|
|
43
|
+
let dbManager;
|
|
44
|
+
let knowledgeGraph;
|
|
45
|
+
let tempDbPath;
|
|
46
|
+
let db;
|
|
47
|
+
let testSessionId;
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
tempDbPath = path.join(os.tmpdir(), `test-kg-integration-${Date.now()}.db`);
|
|
50
|
+
dbManager = new database_1.DatabaseManager({
|
|
51
|
+
filename: tempDbPath,
|
|
52
|
+
maxSize: 10 * 1024 * 1024,
|
|
53
|
+
walMode: true,
|
|
54
|
+
});
|
|
55
|
+
db = dbManager.getDatabase();
|
|
56
|
+
knowledgeGraph = new knowledge_graph_1.KnowledgeGraphManager(db);
|
|
57
|
+
// Create test session
|
|
58
|
+
testSessionId = (0, uuid_1.v4)();
|
|
59
|
+
db.prepare('INSERT INTO sessions (id, name, description) VALUES (?, ?, ?)').run(testSessionId, 'KG Integration Test', 'Testing knowledge graph integration');
|
|
60
|
+
});
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
dbManager.close();
|
|
63
|
+
try {
|
|
64
|
+
fs.unlinkSync(tempDbPath);
|
|
65
|
+
fs.unlinkSync(`${tempDbPath}-wal`);
|
|
66
|
+
fs.unlinkSync(`${tempDbPath}-shm`);
|
|
67
|
+
}
|
|
68
|
+
catch (_e) {
|
|
69
|
+
// Ignore
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
describe('context_analyze', () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
// Add some context items to analyze
|
|
75
|
+
const items = [
|
|
76
|
+
{
|
|
77
|
+
key: 'task_auth',
|
|
78
|
+
value: 'Working on AuthService class that implements authentication using JWT tokens',
|
|
79
|
+
category: 'task',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: 'decision_db',
|
|
83
|
+
value: 'UserModel extends BaseModel and uses PostgreSQL database',
|
|
84
|
+
category: 'decision',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: 'progress_api',
|
|
88
|
+
value: 'The function validateToken calls checkExpiry and getUserById',
|
|
89
|
+
category: 'progress',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: 'note_files',
|
|
93
|
+
value: 'Modified files: auth.service.ts, user.model.ts, and token.utils.ts',
|
|
94
|
+
category: 'note',
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
items.forEach(item => {
|
|
98
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value, category) VALUES (?, ?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, item.key, item.value, item.category);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
it('should analyze all context items and extract entities', () => {
|
|
102
|
+
// Simulate context_analyze tool call
|
|
103
|
+
const items = db
|
|
104
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ?')
|
|
105
|
+
.all(testSessionId);
|
|
106
|
+
let entitiesCreated = 0;
|
|
107
|
+
let _relationsCreated = 0;
|
|
108
|
+
for (const item of items) {
|
|
109
|
+
const analysis = knowledgeGraph.analyzeContext(testSessionId, item.value);
|
|
110
|
+
for (const entityData of analysis.entities) {
|
|
111
|
+
const existing = knowledgeGraph.findEntity(testSessionId, entityData.name, entityData.type);
|
|
112
|
+
if (!existing) {
|
|
113
|
+
knowledgeGraph.createEntity(testSessionId, entityData.type, entityData.name, {
|
|
114
|
+
confidence: entityData.confidence,
|
|
115
|
+
source: item.key,
|
|
116
|
+
});
|
|
117
|
+
entitiesCreated++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const relationData of analysis.relations) {
|
|
121
|
+
const subject = knowledgeGraph.findEntity(testSessionId, relationData.subject);
|
|
122
|
+
const object = knowledgeGraph.findEntity(testSessionId, relationData.object);
|
|
123
|
+
if (subject && object) {
|
|
124
|
+
knowledgeGraph.createRelation(testSessionId, subject.id, relationData.predicate, object.id, relationData.confidence);
|
|
125
|
+
_relationsCreated++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
expect(entitiesCreated).toBeGreaterThan(0);
|
|
130
|
+
// Verify specific entities were created
|
|
131
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'AuthService')).toBeDefined();
|
|
132
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'auth.service.ts')).toBeDefined();
|
|
133
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'validateToken')).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
it('should analyze specific categories only', () => {
|
|
136
|
+
const categories = ['task', 'decision'];
|
|
137
|
+
const items = db
|
|
138
|
+
.prepare(`SELECT * FROM context_items WHERE session_id = ? AND category IN (${categories.map(() => '?').join(',')})`)
|
|
139
|
+
.all(testSessionId, ...categories);
|
|
140
|
+
expect(items).toHaveLength(2);
|
|
141
|
+
for (const item of items) {
|
|
142
|
+
const analysis = knowledgeGraph.analyzeContext(testSessionId, item.value);
|
|
143
|
+
expect(analysis.entities.length).toBeGreaterThan(0);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('context_find_related', () => {
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
// Create a network of entities
|
|
150
|
+
const authService = knowledgeGraph.createEntity(testSessionId, 'class', 'AuthService');
|
|
151
|
+
const userModel = knowledgeGraph.createEntity(testSessionId, 'class', 'UserModel');
|
|
152
|
+
const tokenUtil = knowledgeGraph.createEntity(testSessionId, 'module', 'TokenUtil');
|
|
153
|
+
const database = knowledgeGraph.createEntity(testSessionId, 'database', 'PostgreSQL');
|
|
154
|
+
const validateFunc = knowledgeGraph.createEntity(testSessionId, 'function', 'validateToken');
|
|
155
|
+
// Create relations
|
|
156
|
+
knowledgeGraph.createRelation(testSessionId, authService.id, 'uses', userModel.id);
|
|
157
|
+
knowledgeGraph.createRelation(testSessionId, authService.id, 'imports', tokenUtil.id);
|
|
158
|
+
knowledgeGraph.createRelation(testSessionId, userModel.id, 'stores_in', database.id);
|
|
159
|
+
knowledgeGraph.createRelation(testSessionId, authService.id, 'contains', validateFunc.id);
|
|
160
|
+
// Add a context item that references AuthService
|
|
161
|
+
db.prepare('INSERT INTO context_items (id, session_id, key, value) VALUES (?, ?, ?, ?)').run((0, uuid_1.v4)(), testSessionId, 'auth_task', 'Working on AuthService authentication');
|
|
162
|
+
});
|
|
163
|
+
it('should find related entities by entity name', () => {
|
|
164
|
+
const entity = knowledgeGraph.findEntity(testSessionId, 'AuthService');
|
|
165
|
+
expect(entity).toBeDefined();
|
|
166
|
+
const connected = knowledgeGraph.getConnectedEntities(entity.id, 2);
|
|
167
|
+
expect(connected.size).toBeGreaterThan(1);
|
|
168
|
+
// Should include AuthService and its connections
|
|
169
|
+
const entities = Array.from(connected).map(id => {
|
|
170
|
+
const e = db.prepare('SELECT * FROM entities WHERE id = ?').get(id);
|
|
171
|
+
return e.name;
|
|
172
|
+
});
|
|
173
|
+
expect(entities).toContain('AuthService');
|
|
174
|
+
expect(entities).toContain('UserModel');
|
|
175
|
+
expect(entities).toContain('TokenUtil');
|
|
176
|
+
expect(entities).toContain('validateToken');
|
|
177
|
+
});
|
|
178
|
+
it('should find related entities by context key', () => {
|
|
179
|
+
const contextItem = db
|
|
180
|
+
.prepare('SELECT * FROM context_items WHERE session_id = ? AND key = ?')
|
|
181
|
+
.get(testSessionId, 'auth_task');
|
|
182
|
+
expect(contextItem).toBeDefined();
|
|
183
|
+
// Extract entity from context
|
|
184
|
+
const analysis = knowledgeGraph.analyzeContext(testSessionId, contextItem.value);
|
|
185
|
+
expect(analysis.entities.length).toBeGreaterThan(0);
|
|
186
|
+
const entity = knowledgeGraph.findEntity(testSessionId, 'AuthService');
|
|
187
|
+
expect(entity).toBeDefined();
|
|
188
|
+
});
|
|
189
|
+
it('should filter by relation types', () => {
|
|
190
|
+
const entity = knowledgeGraph.findEntity(testSessionId, 'AuthService');
|
|
191
|
+
const relations = knowledgeGraph.getRelations(entity.id);
|
|
192
|
+
const usesRelations = relations.filter(r => r.predicate === 'uses');
|
|
193
|
+
const importsRelations = relations.filter(r => r.predicate === 'imports');
|
|
194
|
+
expect(usesRelations.length).toBeGreaterThan(0);
|
|
195
|
+
expect(importsRelations.length).toBeGreaterThan(0);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
describe('context_visualize', () => {
|
|
199
|
+
beforeEach(() => {
|
|
200
|
+
// Create entities for visualization
|
|
201
|
+
knowledgeGraph.createEntity(testSessionId, 'class', 'User');
|
|
202
|
+
knowledgeGraph.createEntity(testSessionId, 'class', 'Product');
|
|
203
|
+
knowledgeGraph.createEntity(testSessionId, 'service', 'AuthService');
|
|
204
|
+
// Create context items with different categories and times
|
|
205
|
+
const now = new Date();
|
|
206
|
+
for (let i = 0; i < 10; i++) {
|
|
207
|
+
const category = ['task', 'decision', 'progress'][i % 3];
|
|
208
|
+
const priority = ['high', 'normal', 'low'][i % 3];
|
|
209
|
+
db.prepare(`INSERT INTO context_items
|
|
210
|
+
(id, session_id, key, value, category, priority, created_at)
|
|
211
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run((0, uuid_1.v4)(), testSessionId, `item_${i}`, `Test item ${i}`, category, priority, new Date(now.getTime() - i * 3600000).toISOString() // 1 hour apart
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
it('should generate graph visualization data', () => {
|
|
216
|
+
const user = knowledgeGraph.findEntity(testSessionId, 'User');
|
|
217
|
+
const auth = knowledgeGraph.findEntity(testSessionId, 'AuthService');
|
|
218
|
+
knowledgeGraph.createRelation(testSessionId, auth.id, 'validates', user.id);
|
|
219
|
+
const graphData = knowledgeGraph.getGraphData(testSessionId);
|
|
220
|
+
expect(graphData.nodes.length).toBeGreaterThan(0);
|
|
221
|
+
expect(graphData.edges.length).toBeGreaterThan(0);
|
|
222
|
+
// Verify structure
|
|
223
|
+
expect(graphData.nodes[0]).toHaveProperty('id');
|
|
224
|
+
expect(graphData.nodes[0]).toHaveProperty('type');
|
|
225
|
+
expect(graphData.nodes[0]).toHaveProperty('name');
|
|
226
|
+
expect(graphData.edges[0]).toHaveProperty('source');
|
|
227
|
+
expect(graphData.edges[0]).toHaveProperty('target');
|
|
228
|
+
expect(graphData.edges[0]).toHaveProperty('predicate');
|
|
229
|
+
});
|
|
230
|
+
it('should generate timeline visualization data', () => {
|
|
231
|
+
const timeline = db
|
|
232
|
+
.prepare(`
|
|
233
|
+
SELECT
|
|
234
|
+
strftime('%Y-%m-%d %H:00', created_at) as hour,
|
|
235
|
+
COUNT(*) as events,
|
|
236
|
+
GROUP_CONCAT(DISTINCT category) as categories
|
|
237
|
+
FROM context_items
|
|
238
|
+
WHERE session_id = ?
|
|
239
|
+
GROUP BY hour
|
|
240
|
+
ORDER BY hour DESC
|
|
241
|
+
LIMIT 24
|
|
242
|
+
`)
|
|
243
|
+
.all(testSessionId);
|
|
244
|
+
expect(timeline.length).toBeGreaterThan(0);
|
|
245
|
+
expect(timeline[0]).toHaveProperty('hour');
|
|
246
|
+
expect(timeline[0]).toHaveProperty('events');
|
|
247
|
+
expect(timeline[0]).toHaveProperty('categories');
|
|
248
|
+
});
|
|
249
|
+
it('should generate heatmap visualization data', () => {
|
|
250
|
+
const heatmap = db
|
|
251
|
+
.prepare(`
|
|
252
|
+
SELECT
|
|
253
|
+
category,
|
|
254
|
+
priority,
|
|
255
|
+
COUNT(*) as count
|
|
256
|
+
FROM context_items
|
|
257
|
+
WHERE session_id = ?
|
|
258
|
+
GROUP BY category, priority
|
|
259
|
+
`)
|
|
260
|
+
.all(testSessionId);
|
|
261
|
+
expect(heatmap.length).toBeGreaterThan(0);
|
|
262
|
+
expect(heatmap[0]).toHaveProperty('category');
|
|
263
|
+
expect(heatmap[0]).toHaveProperty('priority');
|
|
264
|
+
expect(heatmap[0]).toHaveProperty('count');
|
|
265
|
+
// Verify we have data for different category/priority combinations
|
|
266
|
+
const categories = new Set(heatmap.map(h => h.category));
|
|
267
|
+
const priorities = new Set(heatmap.map(h => h.priority));
|
|
268
|
+
expect(categories.size).toBeGreaterThanOrEqual(2);
|
|
269
|
+
expect(priorities.size).toBeGreaterThanOrEqual(2);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
describe('Complex scenarios', () => {
|
|
273
|
+
it('should handle code analysis workflow', () => {
|
|
274
|
+
// Simulate analyzing a codebase
|
|
275
|
+
const codeContext = [
|
|
276
|
+
'The AuthController class handles authentication endpoints',
|
|
277
|
+
'It uses AuthService which implements IAuthService interface',
|
|
278
|
+
'AuthService calls UserRepository to fetch user data',
|
|
279
|
+
'The validateToken function uses jwt.verify from jsonwebtoken library',
|
|
280
|
+
'Files involved: auth.controller.ts, auth.service.ts, user.repository.ts',
|
|
281
|
+
];
|
|
282
|
+
// Analyze each piece of context
|
|
283
|
+
const allEntities = new Set();
|
|
284
|
+
const allRelations = [];
|
|
285
|
+
codeContext.forEach(text => {
|
|
286
|
+
const analysis = knowledgeGraph.analyzeContext(testSessionId, text);
|
|
287
|
+
analysis.entities.forEach(e => {
|
|
288
|
+
if (!knowledgeGraph.findEntity(testSessionId, e.name, e.type)) {
|
|
289
|
+
knowledgeGraph.createEntity(testSessionId, e.type, e.name);
|
|
290
|
+
allEntities.add(e.name);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
analysis.relations.forEach(r => {
|
|
294
|
+
const subject = knowledgeGraph.findEntity(testSessionId, r.subject);
|
|
295
|
+
const object = knowledgeGraph.findEntity(testSessionId, r.object);
|
|
296
|
+
if (subject && object) {
|
|
297
|
+
knowledgeGraph.createRelation(testSessionId, subject.id, r.predicate, object.id, r.confidence);
|
|
298
|
+
allRelations.push(r);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
// Verify entities were extracted
|
|
303
|
+
expect(allEntities.size).toBeGreaterThan(5);
|
|
304
|
+
expect(allEntities).toContain('AuthController');
|
|
305
|
+
expect(allEntities).toContain('AuthService');
|
|
306
|
+
expect(allEntities).toContain('auth.controller.ts');
|
|
307
|
+
// Verify relationships
|
|
308
|
+
expect(allRelations.length).toBeGreaterThan(0);
|
|
309
|
+
// Test finding related entities
|
|
310
|
+
const authService = knowledgeGraph.findEntity(testSessionId, 'AuthService');
|
|
311
|
+
if (authService) {
|
|
312
|
+
const connected = knowledgeGraph.getConnectedEntities(authService.id, 2);
|
|
313
|
+
expect(connected.size).toBeGreaterThan(1);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
it('should maintain graph consistency across operations', () => {
|
|
317
|
+
// Create initial graph
|
|
318
|
+
const module1 = knowledgeGraph.createEntity(testSessionId, 'module', 'CoreModule');
|
|
319
|
+
const module2 = knowledgeGraph.createEntity(testSessionId, 'module', 'UtilsModule');
|
|
320
|
+
const service = knowledgeGraph.createEntity(testSessionId, 'service', 'DataService');
|
|
321
|
+
knowledgeGraph.createRelation(testSessionId, module1.id, 'imports', module2.id);
|
|
322
|
+
knowledgeGraph.createRelation(testSessionId, module1.id, 'provides', service.id);
|
|
323
|
+
// Add observations
|
|
324
|
+
knowledgeGraph.addObservation(service.id, 'Handles data transformation');
|
|
325
|
+
// Verify graph integrity
|
|
326
|
+
const graphData = knowledgeGraph.getGraphData(testSessionId);
|
|
327
|
+
expect(graphData.nodes).toHaveLength(3);
|
|
328
|
+
expect(graphData.edges).toHaveLength(2);
|
|
329
|
+
// Test pruning doesn't remove entities with relations
|
|
330
|
+
const pruned = knowledgeGraph.pruneOrphanedEntities(testSessionId);
|
|
331
|
+
expect(pruned).toBe(0);
|
|
332
|
+
// All entities should still exist
|
|
333
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'CoreModule')).toBeDefined();
|
|
334
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'UtilsModule')).toBeDefined();
|
|
335
|
+
expect(knowledgeGraph.findEntity(testSessionId, 'DataService')).toBeDefined();
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|