server-memory-enhanced 2.3.0 → 2.3.2
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 +245 -44
- package/dist/index.js +27 -1
- package/dist/lib/analysis/analytics-service.js +111 -0
- package/dist/lib/analysis/conflict-detector.js +48 -0
- package/dist/lib/analysis/context-builder.js +31 -0
- package/dist/lib/analysis/memory-stats.js +63 -0
- package/dist/lib/analysis/path-finder.js +77 -0
- package/dist/lib/collaboration/conversation-service.js +43 -0
- package/dist/lib/collaboration/flag-manager.js +38 -0
- package/dist/lib/jsonl-storage-adapter.js +8 -4
- package/dist/lib/knowledge-graph-manager.js +94 -663
- package/dist/lib/maintenance/bulk-updater.js +44 -0
- package/dist/lib/maintenance/memory-pruner.js +48 -0
- package/dist/lib/operations/entity-operations.js +27 -0
- package/dist/lib/operations/observation-operations.js +105 -0
- package/dist/lib/operations/relation-operations.js +45 -0
- package/dist/lib/queries/entity-queries.js +68 -0
- package/dist/lib/queries/graph-reader.js +9 -0
- package/dist/lib/queries/search-service.js +99 -0
- package/dist/lib/schemas.js +15 -0
- package/dist/lib/utils/entity-finder.js +31 -0
- package/dist/lib/utils/negation-detector.js +23 -0
- package/dist/lib/utils/observation-validator.js +43 -0
- package/dist/lib/utils/relation-key.js +16 -0
- package/dist/lib/validation/entity-type-validator.js +32 -0
- package/dist/lib/validation/observation-validator.js +61 -0
- package/dist/lib/validation/quality-scorer.js +27 -0
- package/dist/lib/validation/relation-validator.js +36 -0
- package/dist/lib/validation/request-validator.js +91 -0
- package/dist/lib/validation.js +8 -200
- package/dist/lib/versioning/observation-history.js +54 -0
- package/package.json +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path finding service for the knowledge graph
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Find the shortest path between two entities in the knowledge graph
|
|
6
|
+
* Uses BFS algorithm with bidirectional search
|
|
7
|
+
*/
|
|
8
|
+
export async function findRelationPath(storage, from, to, maxDepth = 5) {
|
|
9
|
+
const graph = await storage.loadGraph();
|
|
10
|
+
if (from === to) {
|
|
11
|
+
return { found: true, path: [from], relations: [] };
|
|
12
|
+
}
|
|
13
|
+
// Build indexes for efficient relation lookup
|
|
14
|
+
const relationsFrom = new Map();
|
|
15
|
+
const relationsTo = new Map();
|
|
16
|
+
for (const rel of graph.relations) {
|
|
17
|
+
if (!relationsFrom.has(rel.from)) {
|
|
18
|
+
relationsFrom.set(rel.from, []);
|
|
19
|
+
}
|
|
20
|
+
relationsFrom.get(rel.from).push(rel);
|
|
21
|
+
if (!relationsTo.has(rel.to)) {
|
|
22
|
+
relationsTo.set(rel.to, []);
|
|
23
|
+
}
|
|
24
|
+
relationsTo.get(rel.to).push(rel);
|
|
25
|
+
}
|
|
26
|
+
// BFS to find shortest path
|
|
27
|
+
const queue = [
|
|
28
|
+
{ entity: from, path: [from], relations: [] }
|
|
29
|
+
];
|
|
30
|
+
const visited = new Set([from]);
|
|
31
|
+
while (queue.length > 0) {
|
|
32
|
+
const current = queue.shift();
|
|
33
|
+
if (current.path.length > maxDepth) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Find all relations connected to current entity (both outgoing and incoming for bidirectional search)
|
|
37
|
+
const outgoing = relationsFrom.get(current.entity) || [];
|
|
38
|
+
const incoming = relationsTo.get(current.entity) || [];
|
|
39
|
+
// Check outgoing relations
|
|
40
|
+
for (const rel of outgoing) {
|
|
41
|
+
if (rel.to === to) {
|
|
42
|
+
return {
|
|
43
|
+
found: true,
|
|
44
|
+
path: [...current.path, rel.to],
|
|
45
|
+
relations: [...current.relations, rel]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!visited.has(rel.to)) {
|
|
49
|
+
visited.add(rel.to);
|
|
50
|
+
queue.push({
|
|
51
|
+
entity: rel.to,
|
|
52
|
+
path: [...current.path, rel.to],
|
|
53
|
+
relations: [...current.relations, rel]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Check incoming relations (traverse backwards)
|
|
58
|
+
for (const rel of incoming) {
|
|
59
|
+
if (rel.from === to) {
|
|
60
|
+
return {
|
|
61
|
+
found: true,
|
|
62
|
+
path: [...current.path, rel.from],
|
|
63
|
+
relations: [...current.relations, rel]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (!visited.has(rel.from)) {
|
|
67
|
+
visited.add(rel.from);
|
|
68
|
+
queue.push({
|
|
69
|
+
entity: rel.from,
|
|
70
|
+
path: [...current.path, rel.from],
|
|
71
|
+
relations: [...current.relations, rel]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { found: false, path: [], relations: [] };
|
|
77
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation service (agent threads)
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* List all conversations (agent threads) with summary information
|
|
6
|
+
*/
|
|
7
|
+
export async function listConversations(storage) {
|
|
8
|
+
const graph = await storage.loadGraph();
|
|
9
|
+
// Group data by agent thread
|
|
10
|
+
const threadMap = new Map();
|
|
11
|
+
// Collect entities by thread
|
|
12
|
+
for (const entity of graph.entities) {
|
|
13
|
+
if (!threadMap.has(entity.agentThreadId)) {
|
|
14
|
+
threadMap.set(entity.agentThreadId, { entities: [], relations: [], timestamps: [] });
|
|
15
|
+
}
|
|
16
|
+
const threadData = threadMap.get(entity.agentThreadId);
|
|
17
|
+
threadData.entities.push(entity);
|
|
18
|
+
threadData.timestamps.push(entity.timestamp);
|
|
19
|
+
}
|
|
20
|
+
// Collect relations by thread
|
|
21
|
+
for (const relation of graph.relations) {
|
|
22
|
+
if (!threadMap.has(relation.agentThreadId)) {
|
|
23
|
+
threadMap.set(relation.agentThreadId, { entities: [], relations: [], timestamps: [] });
|
|
24
|
+
}
|
|
25
|
+
const threadData = threadMap.get(relation.agentThreadId);
|
|
26
|
+
threadData.relations.push(relation);
|
|
27
|
+
threadData.timestamps.push(relation.timestamp);
|
|
28
|
+
}
|
|
29
|
+
// Build conversation summaries
|
|
30
|
+
const conversations = Array.from(threadMap.entries()).map(([agentThreadId, data]) => {
|
|
31
|
+
const timestamps = data.timestamps.sort((a, b) => a.localeCompare(b));
|
|
32
|
+
return {
|
|
33
|
+
agentThreadId,
|
|
34
|
+
entityCount: data.entities.length,
|
|
35
|
+
relationCount: data.relations.length,
|
|
36
|
+
firstCreated: timestamps[0] || '',
|
|
37
|
+
lastUpdated: timestamps[timestamps.length - 1] || ''
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
// Sort by last updated (most recent first)
|
|
41
|
+
conversations.sort((a, b) => b.lastUpdated.localeCompare(a.lastUpdated));
|
|
42
|
+
return { conversations };
|
|
43
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collaboration features (flagging, review)
|
|
3
|
+
*/
|
|
4
|
+
import { randomUUID } from 'crypto';
|
|
5
|
+
/**
|
|
6
|
+
* Flag an entity for review
|
|
7
|
+
*/
|
|
8
|
+
export async function flagForReview(storage, entityName, reason, reviewer) {
|
|
9
|
+
const graph = await storage.loadGraph();
|
|
10
|
+
const entity = graph.entities.find(e => e.name === entityName);
|
|
11
|
+
if (!entity) {
|
|
12
|
+
throw new Error(`Entity with name ${entityName} not found`);
|
|
13
|
+
}
|
|
14
|
+
// Add a special observation to mark for review
|
|
15
|
+
const flagContent = `[FLAGGED FOR REVIEW: ${reason}${reviewer ? ` - Reviewer: ${reviewer}` : ''}]`;
|
|
16
|
+
// Check if this flag already exists (by content)
|
|
17
|
+
if (!entity.observations.some(o => o.content === flagContent)) {
|
|
18
|
+
const flagObservation = {
|
|
19
|
+
id: `obs_${randomUUID()}`,
|
|
20
|
+
content: flagContent,
|
|
21
|
+
timestamp: new Date().toISOString(),
|
|
22
|
+
version: 1,
|
|
23
|
+
agentThreadId: entity.agentThreadId,
|
|
24
|
+
confidence: 1.0, // Flag observations have full confidence
|
|
25
|
+
importance: 1.0 // Flag observations are highly important
|
|
26
|
+
};
|
|
27
|
+
entity.observations.push(flagObservation);
|
|
28
|
+
entity.timestamp = new Date().toISOString();
|
|
29
|
+
await storage.saveGraph(graph);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get all entities flagged for review
|
|
34
|
+
*/
|
|
35
|
+
export async function getFlaggedEntities(storage) {
|
|
36
|
+
const graph = await storage.loadGraph();
|
|
37
|
+
return graph.entities.filter(e => e.observations.some(obs => obs.content.includes('[FLAGGED FOR REVIEW:')));
|
|
38
|
+
}
|
|
@@ -240,10 +240,14 @@ export class JsonlStorageAdapter {
|
|
|
240
240
|
* Serialize thread data to JSONL lines
|
|
241
241
|
*/
|
|
242
242
|
serializeThreadData(threadData) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
243
|
+
const lines = [];
|
|
244
|
+
for (const e of threadData.entities) {
|
|
245
|
+
lines.push(this.serializeEntity(e));
|
|
246
|
+
}
|
|
247
|
+
for (const r of threadData.relations) {
|
|
248
|
+
lines.push(this.serializeRelation(r));
|
|
249
|
+
}
|
|
250
|
+
return lines;
|
|
247
251
|
}
|
|
248
252
|
/**
|
|
249
253
|
* Save data for a specific thread
|