refactor: Replace knowledge graph with session clustering system

Remove legacy knowledge graph and evolution tracking features,
introduce new session clustering model for Core Memory.

Changes:
- Remove knowledge_graph, knowledge_graph_edges, evolution_history tables
- Add session_clusters, cluster_members, cluster_relations tables
- Add session_metadata_cache for metadata caching
- Add new interfaces: SessionCluster, ClusterMember, ClusterRelation, SessionMetadataCache
- Add CRUD methods for cluster management
- Add session metadata upsert and search methods
- Remove extractKnowledgeGraph, getKnowledgeGraph, trackEvolution methods
- Remove API routes for /knowledge-graph, /evolution, /graph
- Add database migration to clean up old tables
- Update generateClusterId() for CLST-* ID format

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-18 19:21:52 +08:00
parent 17af615fe2
commit 68f9de0c69
4 changed files with 419 additions and 365 deletions

View File

@@ -1,7 +1,7 @@
import * as http from 'http';
import { URL } from 'url';
import { getCoreMemoryStore } from '../core-memory-store.js';
import type { CoreMemory, KnowledgeGraph, EvolutionVersion } from '../core-memory-store.js';
import type { CoreMemory } from '../core-memory-store.js';
/**
* Route context interface
@@ -197,142 +197,5 @@ export async function handleCoreMemoryRoutes(ctx: RouteContext): Promise<boolean
return true;
}
// API: Core Memory - Extract knowledge graph
if (pathname.startsWith('/api/core-memory/memories/') && pathname.endsWith('/knowledge-graph') && req.method === 'GET') {
const memoryId = pathname.replace('/api/core-memory/memories/', '').replace('/knowledge-graph', '');
const projectPath = url.searchParams.get('path') || initialPath;
try {
const store = getCoreMemoryStore(projectPath);
const knowledgeGraph = store.getKnowledgeGraph(memoryId);
// If no graph exists, extract it first
if (knowledgeGraph.nodes.length === 0) {
const extracted = store.extractKnowledgeGraph(memoryId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, knowledgeGraph: extracted }));
} else {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, knowledgeGraph }));
}
} catch (error: unknown) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));
}
return true;
}
// API: Core Memory - Track evolution history
if (pathname.startsWith('/api/core-memory/memories/') && pathname.endsWith('/evolution') && req.method === 'GET') {
const memoryId = pathname.replace('/api/core-memory/memories/', '').replace('/evolution', '');
const projectPath = url.searchParams.get('path') || initialPath;
try {
const store = getCoreMemoryStore(projectPath);
const evolution = store.trackEvolution(memoryId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, evolution }));
} catch (error: unknown) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));
}
return true;
}
// API: Core Memory - Get aggregated graph data for graph explorer
if (pathname === '/api/core-memory/graph' && req.method === 'GET') {
const projectPath = url.searchParams.get('path') || initialPath;
try {
const store = getCoreMemoryStore(projectPath);
const memories = store.getMemories({ archived: false });
// Aggregate all knowledge graphs from memories
const aggregatedNodes: Array<{
id: string;
name: string;
type: string;
symbol_type?: string;
path?: string;
line_number?: number;
imports?: number;
exports?: number;
references?: number;
}> = [];
const aggregatedEdges: Array<{
source: string;
target: string;
type: string;
weight: number;
}> = [];
const nodeMap = new Map<string, any>();
const edgeMap = new Map<string, any>();
// Collect nodes and edges from all memories
memories.forEach((memory: CoreMemory) => {
const graph = store.getKnowledgeGraph(memory.id);
// Process nodes
graph.nodes.forEach((node: any) => {
const nodeId = node.id || node.name;
if (!nodeMap.has(nodeId)) {
nodeMap.set(nodeId, {
id: nodeId,
name: node.name || node.label || nodeId,
type: node.type || 'MODULE',
symbol_type: node.symbol_type,
path: node.path || node.file_path,
line_number: node.line_number,
imports: node.imports || 0,
exports: node.exports || 0,
references: node.references || 0
});
} else {
// Aggregate counts for duplicate nodes
const existing = nodeMap.get(nodeId);
existing.imports = (existing.imports || 0) + (node.imports || 0);
existing.exports = (existing.exports || 0) + (node.exports || 0);
existing.references = (existing.references || 0) + (node.references || 0);
}
});
// Process edges
graph.edges.forEach((edge: any) => {
const edgeKey = `${edge.source}-${edge.target}-${edge.type || 'CALLS'}`;
if (!edgeMap.has(edgeKey)) {
edgeMap.set(edgeKey, {
source: edge.source || edge.from,
target: edge.target || edge.to,
type: edge.type || edge.relation_type || 'CALLS',
weight: edge.weight || 1
});
} else {
// Aggregate weights for duplicate edges
const existing = edgeMap.get(edgeKey);
existing.weight = (existing.weight || 1) + (edge.weight || 1);
}
});
});
// Convert maps to arrays
aggregatedNodes.push(...nodeMap.values());
aggregatedEdges.push(...edgeMap.values());
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
nodes: aggregatedNodes,
edges: aggregatedEdges
}));
} catch (error: unknown) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: (error as Error).message }));
}
return true;
}
return false;
}