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
- // Check if entity exists first
848
- const entityExists = this.db.prepare(`
849
- SELECT id FROM entities WHERE id = ?
850
- `).get(entityId);
851
- if (!entityExists) {
852
- console.warn(`⚠️ Entity '${name}' not found, skipping`);
853
- continue;
854
- }
855
- // Step 0: Delete entity embeddings
856
- const embeddingMetadata = this.db.prepare(`
857
- SELECT rowid FROM entity_embedding_metadata WHERE entity_id = ?
858
- `).get(entityId);
859
- if (embeddingMetadata) {
860
- const embeddings = this.db.prepare(`
861
- DELETE FROM entity_embeddings WHERE rowid = ?
862
- `).run(embeddingMetadata.rowid);
863
- const metadata = this.db.prepare(`
864
- DELETE FROM entity_embedding_metadata WHERE entity_id = ?
865
- `).run(entityId);
866
- if (embeddings.changes > 0 || metadata.changes > 0) {
867
- console.error(` ├─ Removed entity embeddings for '${name}'`);
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
- // Step 1: Delete chunk-entity associations
871
- const chunkAssociations = this.db.prepare(`
872
- DELETE FROM chunk_entities WHERE entity_id = ?
873
- `).run(entityId);
874
- if (chunkAssociations.changes > 0) {
875
- console.error(` ├─ Removed ${chunkAssociations.changes} chunk associations for '${name}'`);
876
- }
877
- // Step 2: Delete relationships where this entity is involved
878
- const relationships = this.db.prepare(`
879
- DELETE FROM relationships
880
- WHERE source_entity = ? OR target_entity = ?
881
- `).run(entityId, entityId);
882
- if (relationships.changes > 0) {
883
- console.error(` ├─ Removed ${relationships.changes} relationships for '${name}'`);
884
- }
885
- // Step 3: Finally delete the entity itself
886
- const entity = this.db.prepare(`
887
- DELETE FROM entities WHERE id = ?
888
- `).run(entityId);
889
- if (entity.changes > 0) {
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(` └─ Entity '${name}' was not deleted (possibly already removed)`);
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
- "name": "rag-memory-epf-mcp",
3
- "version": "6.0.1",
4
- "engines": {
5
- "node": ">=24"
6
- },
7
- "description": "Project-local RAG memory MCP server 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/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
- }
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
+ }