rag-memory-epf-mcp 6.0.1 → 6.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/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { getAllMCPTools, validateToolArgs, getSystemInfo } from './src/tools/too
|
|
|
22
22
|
// Import migration system
|
|
23
23
|
import { MigrationManager } from './src/migrations/migration-manager.js';
|
|
24
24
|
import { backupBeforeMigration } from './src/backup/preflight.js';
|
|
25
|
-
import { rebuildProjection, deleteStaleKgChunks } from './src/observations/projection.js';
|
|
25
|
+
import { rebuildProjection, deleteStaleKgChunks, deleteKgRelationshipChunks } from './src/observations/projection.js';
|
|
26
26
|
import { addRevision, correctRevision, transitionStatus, linkSources, nextProjectionOrder } from './src/observations/lifecycle.js';
|
|
27
27
|
import { getObservationHistory } from './src/observations/history.js';
|
|
28
28
|
// Import chunk text algorithm (extracted for publish-time invariant testing)
|
|
@@ -843,58 +843,76 @@ export class RAGKnowledgeGraphManager {
|
|
|
843
843
|
console.error(`🗑️ Deleting entities: ${entityNames.join(', ')}`);
|
|
844
844
|
for (const name of entityNames) {
|
|
845
845
|
const entityId = `entity_${name.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '_')}`;
|
|
846
|
+
// One entity = one transaction. The four deletion steps are a single unit:
|
|
847
|
+
// a mid-sequence failure must roll back, not leave embeddings/links purged while
|
|
848
|
+
// the entity itself survives (2026-08-24 audit finding; regression =
|
|
849
|
+
// test/delete-entities-kg-hygiene.test.mjs ⓒ). Batch semantics are preserved —
|
|
850
|
+
// other entities still proceed when one fails.
|
|
846
851
|
try {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
852
|
+
const deleted = this.db.transaction((eid) => {
|
|
853
|
+
// Check if entity exists first
|
|
854
|
+
const entityExists = this.db.prepare(`
|
|
855
|
+
SELECT id FROM entities WHERE id = ?
|
|
856
|
+
`).get(eid);
|
|
857
|
+
if (!entityExists)
|
|
858
|
+
return false;
|
|
859
|
+
// Step 0: Delete entity embeddings
|
|
860
|
+
const embeddingMetadata = this.db.prepare(`
|
|
861
|
+
SELECT rowid FROM entity_embedding_metadata WHERE entity_id = ?
|
|
862
|
+
`).get(eid);
|
|
863
|
+
if (embeddingMetadata) {
|
|
864
|
+
const embeddings = this.db.prepare(`
|
|
865
|
+
DELETE FROM entity_embeddings WHERE rowid = ?
|
|
866
|
+
`).run(embeddingMetadata.rowid);
|
|
867
|
+
const metadata = this.db.prepare(`
|
|
868
|
+
DELETE FROM entity_embedding_metadata WHERE entity_id = ?
|
|
869
|
+
`).run(eid);
|
|
870
|
+
if (embeddings.changes > 0 || metadata.changes > 0) {
|
|
871
|
+
console.error(` ├─ Removed entity embeddings for '${name}'`);
|
|
872
|
+
}
|
|
868
873
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
874
|
+
// Step 1: Delete chunk-entity associations
|
|
875
|
+
const chunkAssociations = this.db.prepare(`
|
|
876
|
+
DELETE FROM chunk_entities WHERE entity_id = ?
|
|
877
|
+
`).run(eid);
|
|
878
|
+
if (chunkAssociations.changes > 0) {
|
|
879
|
+
console.error(` ├─ Removed ${chunkAssociations.changes} chunk associations for '${name}'`);
|
|
880
|
+
}
|
|
881
|
+
// Step 2: Capture relationship ids BEFORE deleting the rows — KG relationship
|
|
882
|
+
// chunks are keyed by relationship_id, so after the DELETE they could no
|
|
883
|
+
// longer be found and would dangle forever.
|
|
884
|
+
const relIds = this.db.prepare(`
|
|
885
|
+
SELECT id FROM relationships
|
|
886
|
+
WHERE source_entity = ? OR target_entity = ?
|
|
887
|
+
`).all(eid, eid);
|
|
888
|
+
const relationships = this.db.prepare(`
|
|
889
|
+
DELETE FROM relationships
|
|
890
|
+
WHERE source_entity = ? OR target_entity = ?
|
|
891
|
+
`).run(eid, eid);
|
|
892
|
+
if (relationships.changes > 0) {
|
|
893
|
+
console.error(` ├─ Removed ${relationships.changes} relationships for '${name}'`);
|
|
894
|
+
}
|
|
895
|
+
// Step 2b: KG hygiene — sweep stale entity chunks and chunks of the captured
|
|
896
|
+
// relationships. The generation path is dormant today (generateKnowledgeGraphChunks
|
|
897
|
+
// is not tool-exposed), but once seeded these chunks stay vector-searchable after
|
|
898
|
+
// deletion unless swept here (same fail-closed rationale as deleteStaleKgChunks).
|
|
899
|
+
deleteStaleKgChunks(this.db, eid);
|
|
900
|
+
deleteKgRelationshipChunks(this.db, relIds.map(r => r.id));
|
|
901
|
+
// Step 3: Finally delete the entity itself (FK CASCADE takes observation lifecycle rows)
|
|
902
|
+
const entity = this.db.prepare(`
|
|
903
|
+
DELETE FROM entities WHERE id = ?
|
|
904
|
+
`).run(eid);
|
|
905
|
+
return entity.changes > 0;
|
|
906
|
+
})(entityId);
|
|
907
|
+
if (deleted) {
|
|
890
908
|
console.error(` └─ Deleted entity '${name}' successfully`);
|
|
891
909
|
}
|
|
892
910
|
else {
|
|
893
|
-
console.warn(
|
|
911
|
+
console.warn(`⚠️ Entity '${name}' not found, skipping`);
|
|
894
912
|
}
|
|
895
913
|
}
|
|
896
914
|
catch (error) {
|
|
897
|
-
console.error(`❌ Failed to delete entity '${name}':`, error);
|
|
915
|
+
console.error(`❌ Failed to delete entity '${name}' (transaction rolled back, continuing):`, error);
|
|
898
916
|
// Continue with other entities instead of failing completely
|
|
899
917
|
}
|
|
900
918
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type Database from 'better-sqlite3';
|
|
2
2
|
export declare function rebuildProjection(db: Database.Database, entityId: string): void;
|
|
3
3
|
export declare function deleteStaleKgChunks(db: Database.Database, entityId: string): number;
|
|
4
|
+
export declare function deleteKgRelationshipChunks(db: Database.Database, relationshipIds: string[]): number;
|
|
@@ -29,3 +29,20 @@ export function deleteStaleKgChunks(db, entityId) {
|
|
|
29
29
|
}
|
|
30
30
|
return n;
|
|
31
31
|
}
|
|
32
|
+
// relationship 청크는 relationship_id 키라 엔티티 삭제만으로는 잡을 수 없다 —
|
|
33
|
+
// 호출자가 relationships 행을 지우기 전에 id 를 캡처해서 넘겨야 한다(deleteEntities 계약).
|
|
34
|
+
// 넘어온 id 중 이미 없는 것은 무시한다(멱등). 벡터 행을 먼저 지우는 것은 deleteStaleKgChunks 와 같다.
|
|
35
|
+
export function deleteKgRelationshipChunks(db, relationshipIds) {
|
|
36
|
+
if (relationshipIds.length === 0)
|
|
37
|
+
return 0;
|
|
38
|
+
let n = 0;
|
|
39
|
+
for (const rid of relationshipIds) {
|
|
40
|
+
const chunks = db.prepare(`SELECT rowid FROM chunk_metadata WHERE chunk_type = 'relationship' AND relationship_id = ?`).all(rid);
|
|
41
|
+
for (const c of chunks) {
|
|
42
|
+
db.exec(`DELETE FROM chunks WHERE rowid = ${Number(c.rowid)}`);
|
|
43
|
+
db.prepare(`DELETE FROM chunk_metadata WHERE rowid = ?`).run(c.rowid);
|
|
44
|
+
n++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return n;
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
2
|
+
"name": "rag-memory-epf-mcp",
|
|
3
|
+
"version": "6.1.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=24"
|
|
6
|
+
},
|
|
7
|
+
"description": "Project-local RAG memory MCP server \u2014 knowledge graph + multilingual vector + FTS5 in a single SQLite file. Per-project isolation, 38 MCP tools, codepoint-safe chunking (Korean/CJK/emoji).",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"mcp",
|
|
10
|
+
"model-context-protocol",
|
|
11
|
+
"rag",
|
|
12
|
+
"knowledge-graph",
|
|
13
|
+
"vector-search",
|
|
14
|
+
"fts5",
|
|
15
|
+
"sqlite",
|
|
16
|
+
"embeddings",
|
|
17
|
+
"bge-m3",
|
|
18
|
+
"multilingual",
|
|
19
|
+
"korean",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"gemini-cli",
|
|
22
|
+
"codex-cli",
|
|
23
|
+
"memory",
|
|
24
|
+
"agent-memory"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "bripin123",
|
|
28
|
+
"homepage": "https://github.com/bripin123/rag-memory-epf-mcp",
|
|
29
|
+
"bugs": "https://github.com/bripin123/rag-memory-epf-mcp/issues",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/bripin123/rag-memory-epf-mcp.git"
|
|
33
|
+
},
|
|
34
|
+
"type": "module",
|
|
35
|
+
"main": "dist/index.js",
|
|
36
|
+
"bin": {
|
|
37
|
+
"rag-memory-mcp": "dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"docs/UPDATING.md"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc && shx chmod +x dist/*.js",
|
|
45
|
+
"prepare": "npm run build",
|
|
46
|
+
"watch": "tsc --watch",
|
|
47
|
+
"verify:invariants": "node test/chunk-invariants.test.mjs",
|
|
48
|
+
"verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs && node test/migration12.test.mjs && node test/model-cache.test.mjs && node test/embedding-gate.test.mjs && node test/lazy-boot.test.mjs && node test/reconciliation.test.mjs && node test/backfill.test.mjs && node test/fts-query.test.mjs && node test/search-contracts.test.mjs && node test/tool-contracts.test.mjs && node test/bounded-exit.test.mjs && node test/observation-schema.test.mjs && node test/search-graph-default.test.mjs && node test/observation-migration.test.mjs && node test/observation-lifecycle.test.mjs && node test/observation-contracts.test.mjs && node test/observation-search.test.mjs && node test/observation-cascade.test.mjs && node test/observation-realdata.test.mjs && node test/chunker-c.test.mjs && node test/migration14.test.mjs && node test/chunk-params-validation.test.mjs && node test/vector-reuse.test.mjs && node test/entity-range-linking.test.mjs && node test/alias-link-gate.test.mjs && node test/entity-name-boundary.test.mjs && node test/stats-chunking.test.mjs && node test/migration14-realdata.test.mjs && node test/migration14-realdata-sync.test.mjs && node test/search-summaries-off.test.mjs && node test/document-return-contracts.test.mjs && node test/observation-date-prefix.test.mjs && node test/delete-entities-cascade.test.mjs && node test/delete-entities-kg-hygiene.test.mjs && node test/backup-publish-portable.test.mjs && node test/graph-context-explain.test.mjs && node test/eval-graph-role-libs.test.mjs && node test/eval-graph-role-t5b.test.mjs && node --test test/eval-graph-role-t8-fix.test.mjs && node --test test/eval-graph-role-t7-upstream.test.mjs && node --test test/eval-graph-role-t11-decision.test.mjs && node --test test/eval-graph-role-prereq-fix.test.mjs",
|
|
49
|
+
"test": "npm run build && npm run verify:invariants && npm run verify:engine",
|
|
50
|
+
"prepublishOnly": "npm run build && npm run verify:invariants && npm run verify:engine"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@huggingface/transformers": "^3.5.1",
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
55
|
+
"better-sqlite3": "^12.8.0",
|
|
56
|
+
"graphology": "^0.26.0",
|
|
57
|
+
"graphology-communities-louvain": "^2.0.2",
|
|
58
|
+
"graphology-metrics": "^2.4.0",
|
|
59
|
+
"graphology-shortest-path": "^2.1.0",
|
|
60
|
+
"graphology-types": "^0.24.8",
|
|
61
|
+
"sqlite-vec": "^0.1.7",
|
|
62
|
+
"tiktoken": "^1.0.17",
|
|
63
|
+
"zod": "^3.25.28"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
67
|
+
"@types/node": "^24",
|
|
68
|
+
"shx": "^0.3.4",
|
|
69
|
+
"typescript": "^5.6.2"
|
|
70
|
+
}
|
|
71
|
+
}
|