memo-grafter 0.2.7 → 0.3.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/README.md +26 -0
- package/USER_GUIDE.md +262 -17
- package/dist/MemoGrafter.d.ts +11 -5
- package/dist/MemoGrafter.d.ts.map +1 -1
- package/dist/MemoGrafter.js +64 -4
- package/dist/MemoGrafter.js.map +1 -1
- package/dist/MemoGrafterAgent.d.ts +8 -1
- package/dist/MemoGrafterAgent.d.ts.map +1 -1
- package/dist/MemoGrafterAgent.js +31 -2
- package/dist/MemoGrafterAgent.js.map +1 -1
- package/dist/adapters/AnthropicAdapter.d.ts +1 -1
- package/dist/adapters/AnthropicAdapter.d.ts.map +1 -1
- package/dist/adapters/GeminiAdapter.d.ts +1 -1
- package/dist/adapters/GeminiAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenAIAdapter.d.ts +1 -1
- package/dist/adapters/OpenAIAdapter.d.ts.map +1 -1
- package/dist/adapters/types.d.ts +1 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/agents/MemoGrafterAgent.d.ts +45 -0
- package/dist/agents/MemoGrafterAgent.d.ts.map +1 -0
- package/dist/agents/MemoGrafterAgent.js +278 -0
- package/dist/agents/MemoGrafterAgent.js.map +1 -0
- package/dist/agents/fleet/ConductorAgent.d.ts +15 -0
- package/dist/agents/fleet/ConductorAgent.d.ts.map +1 -0
- package/dist/agents/fleet/ConductorAgent.js +41 -0
- package/dist/agents/fleet/ConductorAgent.js.map +1 -0
- package/dist/agents/fleet/FleetStore.d.ts +12 -0
- package/dist/agents/fleet/FleetStore.d.ts.map +1 -0
- package/dist/agents/fleet/FleetStore.js +30 -0
- package/dist/agents/fleet/FleetStore.js.map +1 -0
- package/dist/agents/fleet/MemoGrafterFleet.d.ts +27 -0
- package/dist/agents/fleet/MemoGrafterFleet.d.ts.map +1 -0
- package/dist/agents/fleet/MemoGrafterFleet.js +87 -0
- package/dist/agents/fleet/MemoGrafterFleet.js.map +1 -0
- package/dist/agents/fleet/WorkerAgent.d.ts +36 -0
- package/dist/agents/fleet/WorkerAgent.d.ts.map +1 -0
- package/dist/agents/fleet/WorkerAgent.js +140 -0
- package/dist/agents/fleet/WorkerAgent.js.map +1 -0
- package/dist/agents/fleet/types.d.ts +58 -0
- package/dist/agents/fleet/types.d.ts.map +1 -0
- package/dist/agents/fleet/types.js +2 -0
- package/dist/agents/fleet/types.js.map +1 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +124 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +6 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +34 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +39 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/config.d.ts +11 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/config.js +92 -0
- package/dist/cli/utils/config.js.map +1 -0
- package/dist/cli/utils/logger.d.ts +7 -0
- package/dist/cli/utils/logger.d.ts.map +1 -0
- package/dist/cli/utils/logger.js +15 -0
- package/dist/cli/utils/logger.js.map +1 -0
- package/dist/core/MemoGrafter.d.ts +46 -0
- package/dist/core/MemoGrafter.d.ts.map +1 -0
- package/dist/core/MemoGrafter.js +236 -0
- package/dist/core/MemoGrafter.js.map +1 -0
- package/dist/core/types.d.ts +319 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/crawler/DecayScoringPass.d.ts.map +1 -1
- package/dist/crawler/DecayScoringPass.js +6 -0
- package/dist/crawler/DecayScoringPass.js.map +1 -1
- package/dist/crawler/memoryMaintenance.d.ts +1 -0
- package/dist/crawler/memoryMaintenance.d.ts.map +1 -1
- package/dist/crawler/memoryMaintenance.js +2 -1
- package/dist/crawler/memoryMaintenance.js.map +1 -1
- package/dist/crawler/types.d.ts +1 -0
- package/dist/crawler/types.d.ts.map +1 -1
- package/dist/fleet/MemoGrafterFleet.d.ts +9 -2
- package/dist/fleet/MemoGrafterFleet.d.ts.map +1 -1
- package/dist/fleet/MemoGrafterFleet.js +39 -1
- package/dist/fleet/MemoGrafterFleet.js.map +1 -1
- package/dist/fleet/WorkerAgent.d.ts +9 -4
- package/dist/fleet/WorkerAgent.d.ts.map +1 -1
- package/dist/fleet/WorkerAgent.js +45 -3
- package/dist/fleet/WorkerAgent.js.map +1 -1
- package/dist/fleet/types.d.ts +21 -2
- package/dist/fleet/types.d.ts.map +1 -1
- package/dist/index.d.ts +13 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/ingestion/IngestQueue.d.ts +17 -0
- package/dist/ingestion/IngestQueue.d.ts.map +1 -0
- package/dist/ingestion/IngestQueue.js +123 -0
- package/dist/ingestion/IngestQueue.js.map +1 -0
- package/dist/ingestion/conversation/IngestPipeline.d.ts +41 -0
- package/dist/ingestion/conversation/IngestPipeline.d.ts.map +1 -0
- package/dist/ingestion/conversation/IngestPipeline.js +189 -0
- package/dist/ingestion/conversation/IngestPipeline.js.map +1 -0
- package/dist/ingestion/conversation/SegmentProcessor.d.ts +20 -0
- package/dist/ingestion/conversation/SegmentProcessor.d.ts.map +1 -0
- package/dist/ingestion/conversation/SegmentProcessor.js +110 -0
- package/dist/ingestion/conversation/SegmentProcessor.js.map +1 -0
- package/dist/ingestion/conversation/TopicDriftDetector.d.ts +40 -0
- package/dist/ingestion/conversation/TopicDriftDetector.d.ts.map +1 -0
- package/dist/ingestion/conversation/TopicDriftDetector.js +222 -0
- package/dist/ingestion/conversation/TopicDriftDetector.js.map +1 -0
- package/dist/maintenance/ConflictDetectionPass.d.ts +6 -0
- package/dist/maintenance/ConflictDetectionPass.d.ts.map +1 -0
- package/dist/maintenance/ConflictDetectionPass.js +46 -0
- package/dist/maintenance/ConflictDetectionPass.js.map +1 -0
- package/dist/maintenance/DecayScoringPass.d.ts +17 -0
- package/dist/maintenance/DecayScoringPass.d.ts.map +1 -0
- package/dist/maintenance/DecayScoringPass.js +79 -0
- package/dist/maintenance/DecayScoringPass.js.map +1 -0
- package/dist/maintenance/MemoGrafterCrawler.d.ts +14 -0
- package/dist/maintenance/MemoGrafterCrawler.d.ts.map +1 -0
- package/dist/maintenance/MemoGrafterCrawler.js +108 -0
- package/dist/maintenance/MemoGrafterCrawler.js.map +1 -0
- package/dist/maintenance/VersioningPass.d.ts +6 -0
- package/dist/maintenance/VersioningPass.d.ts.map +1 -0
- package/dist/maintenance/VersioningPass.js +40 -0
- package/dist/maintenance/VersioningPass.js.map +1 -0
- package/dist/maintenance/decayScoring.d.ts +7 -0
- package/dist/maintenance/decayScoring.d.ts.map +1 -0
- package/dist/maintenance/decayScoring.js +9 -0
- package/dist/maintenance/decayScoring.js.map +1 -0
- package/dist/maintenance/index.d.ts +7 -0
- package/dist/maintenance/index.d.ts.map +1 -0
- package/dist/maintenance/index.js +5 -0
- package/dist/maintenance/index.js.map +1 -0
- package/dist/maintenance/memoryMaintenance.d.ts +23 -0
- package/dist/maintenance/memoryMaintenance.d.ts.map +1 -0
- package/dist/maintenance/memoryMaintenance.js +141 -0
- package/dist/maintenance/memoryMaintenance.js.map +1 -0
- package/dist/maintenance/types.d.ts +65 -0
- package/dist/maintenance/types.d.ts.map +1 -0
- package/dist/maintenance/types.js +2 -0
- package/dist/maintenance/types.js.map +1 -0
- package/dist/pipeline/GrafterPipeline.d.ts +2 -0
- package/dist/pipeline/GrafterPipeline.d.ts.map +1 -1
- package/dist/pipeline/GrafterPipeline.js +33 -7
- package/dist/pipeline/GrafterPipeline.js.map +1 -1
- package/dist/pipeline/RetrieverPipeline.d.ts +1 -0
- package/dist/pipeline/RetrieverPipeline.d.ts.map +1 -1
- package/dist/pipeline/RetrieverPipeline.js +16 -5
- package/dist/pipeline/RetrieverPipeline.js.map +1 -1
- package/dist/prompts/factRetrievalPrompt.d.ts +1 -1
- package/dist/prompts/factRetrievalPrompt.d.ts.map +1 -1
- package/dist/prompts/historyCompressionPrompt.d.ts +1 -1
- package/dist/prompts/historyCompressionPrompt.d.ts.map +1 -1
- package/dist/prompts/intentShiftPrompt.d.ts +1 -1
- package/dist/prompts/intentShiftPrompt.d.ts.map +1 -1
- package/dist/prompts/memoryInjectionPrompt.d.ts +1 -1
- package/dist/prompts/memoryInjectionPrompt.d.ts.map +1 -1
- package/dist/prompts/segmentExtractionPrompt.d.ts +1 -1
- package/dist/prompts/segmentExtractionPrompt.d.ts.map +1 -1
- package/dist/retrieval/GrafterPipeline.d.ts +26 -0
- package/dist/retrieval/GrafterPipeline.d.ts.map +1 -0
- package/dist/retrieval/GrafterPipeline.js +117 -0
- package/dist/retrieval/GrafterPipeline.js.map +1 -0
- package/dist/retrieval/RetrieverPipeline.d.ts +28 -0
- package/dist/retrieval/RetrieverPipeline.d.ts.map +1 -0
- package/dist/retrieval/RetrieverPipeline.js +160 -0
- package/dist/retrieval/RetrieverPipeline.js.map +1 -0
- package/dist/schema/builders.d.ts +40 -0
- package/dist/schema/builders.d.ts.map +1 -0
- package/dist/schema/builders.js +10 -0
- package/dist/schema/builders.js.map +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/mg-tables.d.ts +7 -0
- package/dist/schema/mg-tables.d.ts.map +1 -0
- package/dist/schema/mg-tables.js +180 -0
- package/dist/schema/mg-tables.js.map +1 -0
- package/dist/store/GraphStore.d.ts +19 -1
- package/dist/store/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.d.ts +33 -1
- package/dist/store/postgres-pgvector/GraphStore.d.ts.map +1 -1
- package/dist/store/postgres-pgvector/GraphStore.js +491 -16
- package/dist/store/postgres-pgvector/GraphStore.js.map +1 -1
- package/dist/tsconfig.cli.tsbuildinfo +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/drift/adaptiveDriftSensitivity.d.ts +1 -1
- package/dist/utils/drift/adaptiveDriftSensitivity.d.ts.map +1 -1
- package/dist/utils/drift/driftScore.d.ts +1 -1
- package/dist/utils/drift/driftScore.d.ts.map +1 -1
- package/dist/utils/drift/driftThreshold.d.ts +1 -1
- package/dist/utils/drift/driftThreshold.d.ts.map +1 -1
- package/dist/utils/drift/reentryMatch.d.ts +1 -1
- package/dist/utils/drift/reentryMatch.d.ts.map +1 -1
- package/dist/utils/extraction/segmentExtraction.d.ts +1 -1
- package/dist/utils/extraction/segmentExtraction.d.ts.map +1 -1
- package/dist/utils/reentry/reentryEdges.d.ts +1 -1
- package/dist/utils/reentry/reentryEdges.d.ts.map +1 -1
- package/dist/utils/reentry/reentrySimilarity.d.ts +1 -1
- package/dist/utils/reentry/reentrySimilarity.d.ts.map +1 -1
- package/dist/utils/reentry/reentryText.d.ts +1 -1
- package/dist/utils/reentry/reentryText.d.ts.map +1 -1
- package/package.json +14 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import postgres, {} from "postgres";
|
|
3
|
+
import { memoGrafterExtensionNames, memoGrafterIndexNames, memoGrafterTableNames } from "../../schema/index.js";
|
|
3
4
|
import { cosineSimilarity } from "../../utils/drift/cosineSimilarity.js";
|
|
4
5
|
import { normalizeTags } from "../../utils/tags.js";
|
|
5
6
|
import { parseVector, toVectorLiteral } from "../../utils/vector/vectorLiteral.js";
|
|
@@ -10,9 +11,33 @@ export class PostgresGraphStore {
|
|
|
10
11
|
max: 10,
|
|
11
12
|
idle_timeout: 30,
|
|
12
13
|
connect_timeout: 10,
|
|
14
|
+
onnotice: () => undefined,
|
|
13
15
|
});
|
|
14
16
|
}
|
|
15
17
|
async initialize() {
|
|
18
|
+
await this.verifySchema();
|
|
19
|
+
}
|
|
20
|
+
async verifySchema() {
|
|
21
|
+
const [extensions, tables] = await Promise.all([
|
|
22
|
+
this.getExistingExtensions(memoGrafterExtensionNames),
|
|
23
|
+
this.getExistingTables(memoGrafterTableNames),
|
|
24
|
+
]);
|
|
25
|
+
const missingExtensions = memoGrafterExtensionNames.filter((name) => !extensions.has(name));
|
|
26
|
+
const missingTables = memoGrafterTableNames.filter((name) => !tables.has(name));
|
|
27
|
+
if (missingExtensions.length === 0 && missingTables.length === 0) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const missing = [
|
|
31
|
+
...missingExtensions.map((name) => `extension ${name}`),
|
|
32
|
+
...missingTables.map((name) => `table ${name}`),
|
|
33
|
+
].join(", ");
|
|
34
|
+
throw new Error(`MemoGrafter database schema is not initialized. Missing ${missing}. `
|
|
35
|
+
+ "Run: npx memo-grafter migrate --db <connection-string>");
|
|
36
|
+
}
|
|
37
|
+
async migrate() {
|
|
38
|
+
const existingExtensions = await this.getExistingExtensions(memoGrafterExtensionNames);
|
|
39
|
+
const existingTables = await this.getExistingTables(memoGrafterTableNames);
|
|
40
|
+
const existingIndexes = await this.getExistingIndexes(memoGrafterIndexNames);
|
|
16
41
|
await this.sql `CREATE EXTENSION IF NOT EXISTS vector`;
|
|
17
42
|
await this.sql `CREATE EXTENSION IF NOT EXISTS pgcrypto`;
|
|
18
43
|
await this.sql `
|
|
@@ -52,6 +77,8 @@ export class PostgresGraphStore {
|
|
|
52
77
|
agent_color TEXT,
|
|
53
78
|
fleet_id TEXT,
|
|
54
79
|
agent_id TEXT,
|
|
80
|
+
suppressed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
81
|
+
suppressed_at TIMESTAMPTZ,
|
|
55
82
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
56
83
|
UNIQUE (segment_id)
|
|
57
84
|
)
|
|
@@ -85,6 +112,8 @@ export class PostgresGraphStore {
|
|
|
85
112
|
source_title TEXT,
|
|
86
113
|
superseded_by UUID REFERENCES mg_memory_nodes(id),
|
|
87
114
|
decayed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
115
|
+
forgotten BOOLEAN NOT NULL DEFAULT FALSE,
|
|
116
|
+
forgotten_at TIMESTAMPTZ,
|
|
88
117
|
has_conflict BOOLEAN NOT NULL DEFAULT FALSE,
|
|
89
118
|
agent_color TEXT,
|
|
90
119
|
fleet_id TEXT,
|
|
@@ -102,6 +131,22 @@ export class PostgresGraphStore {
|
|
|
102
131
|
await this.sql `
|
|
103
132
|
ALTER TABLE mg_memory_nodes
|
|
104
133
|
ADD COLUMN IF NOT EXISTS has_conflict BOOLEAN NOT NULL DEFAULT FALSE
|
|
134
|
+
`;
|
|
135
|
+
await this.sql `
|
|
136
|
+
ALTER TABLE mg_memory_nodes
|
|
137
|
+
ADD COLUMN IF NOT EXISTS forgotten BOOLEAN NOT NULL DEFAULT FALSE
|
|
138
|
+
`;
|
|
139
|
+
await this.sql `
|
|
140
|
+
ALTER TABLE mg_memory_nodes
|
|
141
|
+
ADD COLUMN IF NOT EXISTS forgotten_at TIMESTAMPTZ
|
|
142
|
+
`;
|
|
143
|
+
await this.sql `
|
|
144
|
+
ALTER TABLE mg_topic_nodes
|
|
145
|
+
ADD COLUMN IF NOT EXISTS suppressed BOOLEAN NOT NULL DEFAULT FALSE
|
|
146
|
+
`;
|
|
147
|
+
await this.sql `
|
|
148
|
+
ALTER TABLE mg_topic_nodes
|
|
149
|
+
ADD COLUMN IF NOT EXISTS suppressed_at TIMESTAMPTZ
|
|
105
150
|
`;
|
|
106
151
|
await this.sql `
|
|
107
152
|
ALTER TABLE mg_topic_nodes
|
|
@@ -157,6 +202,20 @@ export class PostgresGraphStore {
|
|
|
157
202
|
`;
|
|
158
203
|
await this.migrateExistingNodeTable();
|
|
159
204
|
await this.createIndexes();
|
|
205
|
+
return {
|
|
206
|
+
extensions: memoGrafterExtensionNames.map((name) => ({
|
|
207
|
+
name,
|
|
208
|
+
status: existingExtensions.has(name) ? "exists" : "created",
|
|
209
|
+
})),
|
|
210
|
+
tables: memoGrafterTableNames.map((name) => ({
|
|
211
|
+
name,
|
|
212
|
+
status: existingTables.has(name) ? "exists" : "created",
|
|
213
|
+
})),
|
|
214
|
+
indexes: memoGrafterIndexNames.map((name) => ({
|
|
215
|
+
name,
|
|
216
|
+
status: existingIndexes.has(name) ? "exists" : "created",
|
|
217
|
+
})),
|
|
218
|
+
};
|
|
160
219
|
}
|
|
161
220
|
async saveMessages(sessionId, messages) {
|
|
162
221
|
await this.saveMessagesAt(sessionId, 0, messages);
|
|
@@ -308,6 +367,7 @@ export class PostgresGraphStore {
|
|
|
308
367
|
JOIN mg_topic_nodes n ON n.id = e.src_id
|
|
309
368
|
WHERE n.session_id = ${sessionId}
|
|
310
369
|
AND e.type = ${type}
|
|
370
|
+
AND n.suppressed = FALSE
|
|
311
371
|
`;
|
|
312
372
|
return rows.map((row) => ({
|
|
313
373
|
srcId: row.src_id,
|
|
@@ -397,6 +457,7 @@ export class PostgresGraphStore {
|
|
|
397
457
|
SELECT COUNT(*)::int AS count
|
|
398
458
|
FROM mg_topic_nodes
|
|
399
459
|
WHERE session_id = ${sessionId}
|
|
460
|
+
AND suppressed = FALSE
|
|
400
461
|
`;
|
|
401
462
|
return rows[0]?.count ?? 0;
|
|
402
463
|
}
|
|
@@ -406,6 +467,7 @@ export class PostgresGraphStore {
|
|
|
406
467
|
const rows = await this.sql `
|
|
407
468
|
SELECT * FROM mg_topic_nodes
|
|
408
469
|
WHERE ${searchTaggedSessions ? this.sql `TRUE` : this.sql `session_id = ${sessionId}`}
|
|
470
|
+
${options.includeSuppressed ? this.sql `` : this.sql `AND suppressed = FALSE`}
|
|
409
471
|
${this.topicTagsFilterSql(tags, options.tagMode)}
|
|
410
472
|
ORDER BY session_id ASC, topic_order ASC, created_at ASC
|
|
411
473
|
`;
|
|
@@ -415,6 +477,7 @@ export class PostgresGraphStore {
|
|
|
415
477
|
const rows = await this.sql `
|
|
416
478
|
SELECT * FROM mg_topic_nodes
|
|
417
479
|
WHERE session_id = ${sessionId}
|
|
480
|
+
AND suppressed = FALSE
|
|
418
481
|
ORDER BY topic_order DESC, created_at DESC
|
|
419
482
|
LIMIT 1
|
|
420
483
|
`;
|
|
@@ -468,11 +531,15 @@ export class PostgresGraphStore {
|
|
|
468
531
|
}
|
|
469
532
|
async getMemoriesByTopic(topicNodeId) {
|
|
470
533
|
const rows = await this.sql `
|
|
471
|
-
SELECT
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
534
|
+
SELECT memory.*
|
|
535
|
+
FROM mg_memory_nodes memory
|
|
536
|
+
JOIN mg_topic_nodes topic ON topic.id = memory.topic_node_id
|
|
537
|
+
WHERE memory.topic_node_id = ${topicNodeId}
|
|
538
|
+
AND memory.decayed = false
|
|
539
|
+
AND memory.superseded_by IS NULL
|
|
540
|
+
AND memory.forgotten = false
|
|
541
|
+
AND topic.suppressed = false
|
|
542
|
+
ORDER BY memory.created_at ASC
|
|
476
543
|
`;
|
|
477
544
|
return rows.map((row) => this.rowToMemoryNode(row));
|
|
478
545
|
}
|
|
@@ -496,13 +563,136 @@ export class PostgresGraphStore {
|
|
|
496
563
|
`;
|
|
497
564
|
return rows.map((row) => this.rowToMemoryEdge(row));
|
|
498
565
|
}
|
|
566
|
+
async getMemoryHistoryById(memoryNodeId, options = {}) {
|
|
567
|
+
const anchor = await this.getMemoryNodeById(memoryNodeId, options.sessionId);
|
|
568
|
+
if (!anchor) {
|
|
569
|
+
return {
|
|
570
|
+
anchorMemoryId: memoryNodeId,
|
|
571
|
+
...(options.sessionId ? { sessionId: options.sessionId } : {}),
|
|
572
|
+
entries: [],
|
|
573
|
+
edges: [],
|
|
574
|
+
currentMemory: null,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const memories = await this.getMemoryNodesByFactKey(anchor.subject, anchor.predicate, {
|
|
578
|
+
sessionId: options.sessionId ?? anchor.sessionId,
|
|
579
|
+
});
|
|
580
|
+
const byId = new Map(memories.map((memory) => [memory.id, memory]));
|
|
581
|
+
byId.set(anchor.id, anchor);
|
|
582
|
+
const firstPassEdges = await this.getMemoryEdgesForMemoryIds([...byId.keys()]);
|
|
583
|
+
for (const id of this.getConnectedMemoryIds(firstPassEdges)) {
|
|
584
|
+
if (!byId.has(id)) {
|
|
585
|
+
byId.set(id, null);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const missingIds = [...byId.entries()]
|
|
589
|
+
.filter(([, memory]) => memory == null)
|
|
590
|
+
.map(([id]) => id);
|
|
591
|
+
for (const memory of await this.getMemoryNodesByIds(missingIds, options.sessionId ?? anchor.sessionId)) {
|
|
592
|
+
byId.set(memory.id, memory);
|
|
593
|
+
}
|
|
594
|
+
const historyMemories = [...byId.values()].filter((memory) => memory != null);
|
|
595
|
+
const edges = await this.getMemoryEdgesForMemoryIds(historyMemories.map((memory) => memory.id));
|
|
596
|
+
return this.buildMemoryHistoryResult(historyMemories, edges, {
|
|
597
|
+
anchorMemoryId: memoryNodeId,
|
|
598
|
+
subject: anchor.subject,
|
|
599
|
+
predicate: anchor.predicate,
|
|
600
|
+
sessionId: options.sessionId ?? anchor.sessionId,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
async getMemoryHistoryByFact(subject, predicate, options = {}) {
|
|
604
|
+
const memories = await this.getMemoryNodesByFactKey(subject, predicate, options);
|
|
605
|
+
const edges = await this.getMemoryEdgesForMemoryIds(memories.map((memory) => memory.id));
|
|
606
|
+
return this.buildMemoryHistoryResult(memories, edges, {
|
|
607
|
+
subject,
|
|
608
|
+
predicate,
|
|
609
|
+
...(options.sessionId ? { sessionId: options.sessionId } : {}),
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
async getMemoryDiff(fromMemoryId, toMemoryId) {
|
|
613
|
+
const [from, to] = await Promise.all([
|
|
614
|
+
this.getMemoryNodeById(fromMemoryId),
|
|
615
|
+
this.getMemoryNodeById(toMemoryId),
|
|
616
|
+
]);
|
|
617
|
+
if (!from) {
|
|
618
|
+
throw new Error(`Memory ${fromMemoryId} was not found.`);
|
|
619
|
+
}
|
|
620
|
+
if (!to) {
|
|
621
|
+
throw new Error(`Memory ${toMemoryId} was not found.`);
|
|
622
|
+
}
|
|
623
|
+
const edges = (await this.getMemoryEdgesForMemoryIds([from.id, to.id]))
|
|
624
|
+
.filter((edge) => (edge.sourceId === from.id && edge.targetId === to.id)
|
|
625
|
+
|| (edge.sourceId === to.id && edge.targetId === from.id));
|
|
626
|
+
const updateEdges = edges.filter((edge) => edge.edgeType === "updates");
|
|
627
|
+
const conflictEdges = edges.filter((edge) => edge.edgeType === "conflicts");
|
|
628
|
+
const fields = this.diffMemoryFields(from, to);
|
|
629
|
+
return {
|
|
630
|
+
from,
|
|
631
|
+
to,
|
|
632
|
+
fields,
|
|
633
|
+
changedFields: fields.filter((field) => field.changed),
|
|
634
|
+
relationship: {
|
|
635
|
+
supersedes: to.supersededBy === from.id || updateEdges.some((edge) => edge.sourceId === from.id && edge.targetId === to.id),
|
|
636
|
+
supersededBy: from.supersededBy === to.id || updateEdges.some((edge) => edge.sourceId === to.id && edge.targetId === from.id),
|
|
637
|
+
conflicts: conflictEdges.length > 0,
|
|
638
|
+
updateEdges,
|
|
639
|
+
conflictEdges,
|
|
640
|
+
},
|
|
641
|
+
};
|
|
642
|
+
}
|
|
499
643
|
async listMemoryNodesForMaintenance() {
|
|
500
644
|
const rows = await this.sql `
|
|
501
|
-
SELECT
|
|
502
|
-
|
|
645
|
+
SELECT memory.*
|
|
646
|
+
FROM mg_memory_nodes memory
|
|
647
|
+
JOIN mg_topic_nodes topic ON topic.id = memory.topic_node_id
|
|
648
|
+
WHERE memory.forgotten = FALSE
|
|
649
|
+
AND topic.suppressed = FALSE
|
|
650
|
+
ORDER BY memory.session_id ASC, memory.created_at ASC, memory.id ASC
|
|
503
651
|
`;
|
|
504
652
|
return rows.map((row) => this.rowToMemoryNode(row));
|
|
505
653
|
}
|
|
654
|
+
async forgetMemory(memoryNodeId) {
|
|
655
|
+
const changed = await this.forgetMemories([memoryNodeId]);
|
|
656
|
+
return changed > 0;
|
|
657
|
+
}
|
|
658
|
+
async forgetMemories(memoryNodeIds) {
|
|
659
|
+
if (memoryNodeIds.length === 0)
|
|
660
|
+
return 0;
|
|
661
|
+
const rows = await this.sql `
|
|
662
|
+
UPDATE mg_memory_nodes
|
|
663
|
+
SET
|
|
664
|
+
forgotten = TRUE,
|
|
665
|
+
forgotten_at = COALESCE(forgotten_at, NOW())
|
|
666
|
+
WHERE id = ANY(${this.sql.array(memoryNodeIds)}::uuid[])
|
|
667
|
+
AND forgotten = FALSE
|
|
668
|
+
RETURNING id
|
|
669
|
+
`;
|
|
670
|
+
return rows.length;
|
|
671
|
+
}
|
|
672
|
+
async suppressTopic(topicNodeId) {
|
|
673
|
+
const rows = await this.sql `
|
|
674
|
+
UPDATE mg_topic_nodes
|
|
675
|
+
SET
|
|
676
|
+
suppressed = TRUE,
|
|
677
|
+
suppressed_at = COALESCE(suppressed_at, NOW())
|
|
678
|
+
WHERE id = ${topicNodeId}
|
|
679
|
+
AND suppressed = FALSE
|
|
680
|
+
RETURNING id
|
|
681
|
+
`;
|
|
682
|
+
return rows.length > 0;
|
|
683
|
+
}
|
|
684
|
+
async restoreTopic(topicNodeId) {
|
|
685
|
+
const rows = await this.sql `
|
|
686
|
+
UPDATE mg_topic_nodes
|
|
687
|
+
SET
|
|
688
|
+
suppressed = FALSE,
|
|
689
|
+
suppressed_at = NULL
|
|
690
|
+
WHERE id = ${topicNodeId}
|
|
691
|
+
AND suppressed = TRUE
|
|
692
|
+
RETURNING id
|
|
693
|
+
`;
|
|
694
|
+
return rows.length > 0;
|
|
695
|
+
}
|
|
506
696
|
async markMemoryNodesConflicting(memoryNodeIds) {
|
|
507
697
|
if (memoryNodeIds.length === 0)
|
|
508
698
|
return 0;
|
|
@@ -572,16 +762,45 @@ export class PostgresGraphStore {
|
|
|
572
762
|
return true;
|
|
573
763
|
}
|
|
574
764
|
async searchMemories(embedding, sessionId, limit, minSimilarity, options = {}) {
|
|
765
|
+
if (options.sessionIds && options.sessionIds.length > 0) {
|
|
766
|
+
return this.searchMemoriesAcrossSessions(embedding, options.sessionIds, limit, minSimilarity, options);
|
|
767
|
+
}
|
|
575
768
|
const tags = normalizeTags(options.tags);
|
|
576
769
|
const searchTaggedSessions = options.scope === "tagged" && tags.length > 0;
|
|
577
770
|
const rows = await this.sql `
|
|
578
|
-
SELECT
|
|
579
|
-
FROM mg_memory_nodes
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
AND
|
|
583
|
-
|
|
584
|
-
AND
|
|
771
|
+
SELECT memory.*, 1 - (memory.embedding <=> ${toVectorLiteral(embedding)}::vector) AS similarity
|
|
772
|
+
FROM mg_memory_nodes memory
|
|
773
|
+
JOIN mg_topic_nodes topic ON topic.id = memory.topic_node_id
|
|
774
|
+
WHERE ${searchTaggedSessions ? this.sql `TRUE` : this.sql `memory.session_id = ${sessionId}`}
|
|
775
|
+
AND memory.decayed = false
|
|
776
|
+
AND memory.superseded_by IS NULL
|
|
777
|
+
AND memory.forgotten = false
|
|
778
|
+
AND topic.suppressed = false
|
|
779
|
+
${this.memoryTagsFilterSql(tags, options.tagMode, "memory")}
|
|
780
|
+
AND 1 - (memory.embedding <=> ${toVectorLiteral(embedding)}::vector) >= ${minSimilarity}
|
|
781
|
+
ORDER BY similarity DESC
|
|
782
|
+
LIMIT ${limit}
|
|
783
|
+
`;
|
|
784
|
+
return rows.map((row) => ({
|
|
785
|
+
...this.rowToMemoryNode(row),
|
|
786
|
+
similarity: row.similarity,
|
|
787
|
+
}));
|
|
788
|
+
}
|
|
789
|
+
async searchMemoriesAcrossSessions(embedding, sessionIds, limit, minSimilarity, options = {}) {
|
|
790
|
+
if (sessionIds.length === 0)
|
|
791
|
+
return [];
|
|
792
|
+
const tags = normalizeTags(options.tags);
|
|
793
|
+
const rows = await this.sql `
|
|
794
|
+
SELECT memory.*, 1 - (memory.embedding <=> ${toVectorLiteral(embedding)}::vector) AS similarity
|
|
795
|
+
FROM mg_memory_nodes memory
|
|
796
|
+
JOIN mg_topic_nodes topic ON topic.id = memory.topic_node_id
|
|
797
|
+
WHERE memory.session_id = ANY(${this.sql.array(sessionIds)})
|
|
798
|
+
AND memory.decayed = false
|
|
799
|
+
AND memory.superseded_by IS NULL
|
|
800
|
+
AND memory.forgotten = false
|
|
801
|
+
AND topic.suppressed = false
|
|
802
|
+
${this.memoryTagsFilterSql(tags, options.tagMode, "memory")}
|
|
803
|
+
AND 1 - (memory.embedding <=> ${toVectorLiteral(embedding)}::vector) >= ${minSimilarity}
|
|
585
804
|
ORDER BY similarity DESC
|
|
586
805
|
LIMIT ${limit}
|
|
587
806
|
`;
|
|
@@ -634,6 +853,22 @@ export class PostgresGraphStore {
|
|
|
634
853
|
SELECT *, 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) AS similarity
|
|
635
854
|
FROM mg_topic_nodes
|
|
636
855
|
WHERE session_id = ${sessionId}
|
|
856
|
+
AND suppressed = FALSE
|
|
857
|
+
${options.excludeNodeId ? this.sql `AND id != ${options.excludeNodeId}` : this.sql ``}
|
|
858
|
+
${options.minSimilarity === undefined ? this.sql `` : this.sql `AND 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) >= ${options.minSimilarity}`}
|
|
859
|
+
ORDER BY embedding <=> ${toVectorLiteral(embedding)}::vector ASC
|
|
860
|
+
LIMIT ${options.k ?? 5}
|
|
861
|
+
`;
|
|
862
|
+
return rows.map((row) => this.rowToNode(row));
|
|
863
|
+
}
|
|
864
|
+
async getSimilarNodesAcrossSessions(embedding, sessionIds, options = {}) {
|
|
865
|
+
if (sessionIds.length === 0)
|
|
866
|
+
return [];
|
|
867
|
+
const rows = await this.sql `
|
|
868
|
+
SELECT *, 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) AS similarity
|
|
869
|
+
FROM mg_topic_nodes
|
|
870
|
+
WHERE session_id = ANY(${this.sql.array(sessionIds)})
|
|
871
|
+
AND suppressed = FALSE
|
|
637
872
|
${options.excludeNodeId ? this.sql `AND id != ${options.excludeNodeId}` : this.sql ``}
|
|
638
873
|
${options.minSimilarity === undefined ? this.sql `` : this.sql `AND 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) >= ${options.minSimilarity}`}
|
|
639
874
|
ORDER BY embedding <=> ${toVectorLiteral(embedding)}::vector ASC
|
|
@@ -646,6 +881,7 @@ export class PostgresGraphStore {
|
|
|
646
881
|
SELECT *, 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) AS similarity
|
|
647
882
|
FROM mg_topic_nodes
|
|
648
883
|
WHERE fleet_id = ${fleetId}
|
|
884
|
+
AND suppressed = FALSE
|
|
649
885
|
${options.agentColor ? this.sql `AND agent_color = ${options.agentColor}` : this.sql ``}
|
|
650
886
|
${options.excludeNodeId ? this.sql `AND id != ${options.excludeNodeId}` : this.sql ``}
|
|
651
887
|
${options.minSimilarity === undefined ? this.sql `` : this.sql `AND 1 - (embedding <=> ${toVectorLiteral(embedding)}::vector) >= ${options.minSimilarity}`}
|
|
@@ -659,6 +895,7 @@ export class PostgresGraphStore {
|
|
|
659
895
|
SELECT * FROM mg_topic_nodes
|
|
660
896
|
WHERE fleet_id = ${fleetId}
|
|
661
897
|
AND agent_color = ${agentColor}
|
|
898
|
+
AND suppressed = FALSE
|
|
662
899
|
ORDER BY topic_order ASC, created_at ASC
|
|
663
900
|
`;
|
|
664
901
|
return rows.map((row) => this.rowToNode(row));
|
|
@@ -703,6 +940,14 @@ export class PostgresGraphStore {
|
|
|
703
940
|
agent_id = ${metadata.agentId},
|
|
704
941
|
agent_color = ${metadata.agentColor}
|
|
705
942
|
WHERE session_id = ${sessionId}
|
|
943
|
+
`;
|
|
944
|
+
await this.sql `
|
|
945
|
+
UPDATE mg_memory_nodes
|
|
946
|
+
SET
|
|
947
|
+
fleet_id = ${metadata.fleetId},
|
|
948
|
+
agent_id = ${metadata.agentId},
|
|
949
|
+
agent_color = ${metadata.agentColor}
|
|
950
|
+
WHERE session_id = ${sessionId}
|
|
706
951
|
`;
|
|
707
952
|
}
|
|
708
953
|
async setSessionTags(sessionId, tags) {
|
|
@@ -724,6 +969,7 @@ export class PostgresGraphStore {
|
|
|
724
969
|
FROM mg_topic_nodes
|
|
725
970
|
WHERE session_id = ${sessionId}
|
|
726
971
|
AND topic_order = ${topicOrder - 1}
|
|
972
|
+
AND suppressed = FALSE
|
|
727
973
|
LIMIT 1
|
|
728
974
|
`;
|
|
729
975
|
return rows[0] ? this.rowToNode(rows[0]) : null;
|
|
@@ -767,6 +1013,7 @@ export class PostgresGraphStore {
|
|
|
767
1013
|
SELECT * FROM mg_topic_nodes
|
|
768
1014
|
WHERE id = ANY(${this.sql.array(ids)})
|
|
769
1015
|
${sessionId ? this.sql `AND session_id = ${sessionId}` : this.sql ``}
|
|
1016
|
+
AND suppressed = FALSE
|
|
770
1017
|
ORDER BY topic_order ASC, created_at ASC
|
|
771
1018
|
`;
|
|
772
1019
|
return rows.map((row) => this.rowToNode(row));
|
|
@@ -825,7 +1072,10 @@ export class PostgresGraphStore {
|
|
|
825
1072
|
const copiedNodes = [];
|
|
826
1073
|
const nextMessageIndex = await this.getNextMessageIndex(targetSessionId);
|
|
827
1074
|
const nextTopicOrder = await this.getNextTopicOrder(targetSessionId);
|
|
828
|
-
for (const
|
|
1075
|
+
for (const node of nodes) {
|
|
1076
|
+
if (node.suppressed)
|
|
1077
|
+
continue;
|
|
1078
|
+
const index = copiedNodes.length;
|
|
829
1079
|
const messageIndex = nextMessageIndex + index;
|
|
830
1080
|
await this.saveMessagesAt(targetSessionId, messageIndex, [{
|
|
831
1081
|
role: "assistant",
|
|
@@ -919,8 +1169,182 @@ export class PostgresGraphStore {
|
|
|
919
1169
|
AND session_id = ${sourceNode.sessionId}
|
|
920
1170
|
AND decayed = FALSE
|
|
921
1171
|
AND superseded_by IS NULL
|
|
1172
|
+
AND forgotten = FALSE
|
|
922
1173
|
`;
|
|
923
1174
|
}
|
|
1175
|
+
async getMemoryNodeById(memoryNodeId, sessionId) {
|
|
1176
|
+
if (sessionId) {
|
|
1177
|
+
const rows = await this.sql `
|
|
1178
|
+
SELECT *
|
|
1179
|
+
FROM mg_memory_nodes
|
|
1180
|
+
WHERE id = ${memoryNodeId}::uuid
|
|
1181
|
+
AND session_id = ${sessionId}
|
|
1182
|
+
LIMIT 1
|
|
1183
|
+
`;
|
|
1184
|
+
return rows[0] ? this.rowToMemoryNode(rows[0]) : null;
|
|
1185
|
+
}
|
|
1186
|
+
const rows = await this.sql `
|
|
1187
|
+
SELECT *
|
|
1188
|
+
FROM mg_memory_nodes
|
|
1189
|
+
WHERE id = ${memoryNodeId}::uuid
|
|
1190
|
+
LIMIT 1
|
|
1191
|
+
`;
|
|
1192
|
+
return rows[0] ? this.rowToMemoryNode(rows[0]) : null;
|
|
1193
|
+
}
|
|
1194
|
+
async getMemoryNodesByIds(memoryNodeIds, sessionId) {
|
|
1195
|
+
if (memoryNodeIds.length === 0)
|
|
1196
|
+
return [];
|
|
1197
|
+
if (sessionId) {
|
|
1198
|
+
const rows = await this.sql `
|
|
1199
|
+
SELECT *
|
|
1200
|
+
FROM mg_memory_nodes
|
|
1201
|
+
WHERE id = ANY(${this.sql.array(memoryNodeIds)}::uuid[])
|
|
1202
|
+
AND session_id = ${sessionId}
|
|
1203
|
+
ORDER BY created_at ASC, id ASC
|
|
1204
|
+
`;
|
|
1205
|
+
return rows.map((row) => this.rowToMemoryNode(row));
|
|
1206
|
+
}
|
|
1207
|
+
const rows = await this.sql `
|
|
1208
|
+
SELECT *
|
|
1209
|
+
FROM mg_memory_nodes
|
|
1210
|
+
WHERE id = ANY(${this.sql.array(memoryNodeIds)}::uuid[])
|
|
1211
|
+
ORDER BY created_at ASC, id ASC
|
|
1212
|
+
`;
|
|
1213
|
+
return rows.map((row) => this.rowToMemoryNode(row));
|
|
1214
|
+
}
|
|
1215
|
+
async getMemoryNodesByFactKey(subject, predicate, options = {}) {
|
|
1216
|
+
const normalizedSubject = this.normalizeMemoryHistoryPart(subject);
|
|
1217
|
+
const normalizedPredicate = this.normalizeMemoryHistoryPart(predicate);
|
|
1218
|
+
if (options.sessionId) {
|
|
1219
|
+
const rows = await this.sql `
|
|
1220
|
+
SELECT *
|
|
1221
|
+
FROM mg_memory_nodes
|
|
1222
|
+
WHERE lower(regexp_replace(trim(subject), '[[:space:]]+', ' ', 'g')) = ${normalizedSubject}
|
|
1223
|
+
AND lower(regexp_replace(trim(predicate), '[[:space:]]+', ' ', 'g')) = ${normalizedPredicate}
|
|
1224
|
+
AND session_id = ${options.sessionId}
|
|
1225
|
+
ORDER BY created_at ASC, id ASC
|
|
1226
|
+
`;
|
|
1227
|
+
return rows.map((row) => this.rowToMemoryNode(row));
|
|
1228
|
+
}
|
|
1229
|
+
const rows = await this.sql `
|
|
1230
|
+
SELECT *
|
|
1231
|
+
FROM mg_memory_nodes
|
|
1232
|
+
WHERE lower(regexp_replace(trim(subject), '[[:space:]]+', ' ', 'g')) = ${normalizedSubject}
|
|
1233
|
+
AND lower(regexp_replace(trim(predicate), '[[:space:]]+', ' ', 'g')) = ${normalizedPredicate}
|
|
1234
|
+
ORDER BY created_at ASC, id ASC
|
|
1235
|
+
`;
|
|
1236
|
+
return rows.map((row) => this.rowToMemoryNode(row));
|
|
1237
|
+
}
|
|
1238
|
+
async getMemoryEdgesForMemoryIds(memoryNodeIds) {
|
|
1239
|
+
if (memoryNodeIds.length === 0)
|
|
1240
|
+
return [];
|
|
1241
|
+
const rows = await this.sql `
|
|
1242
|
+
SELECT DISTINCT *
|
|
1243
|
+
FROM mg_memory_edges
|
|
1244
|
+
WHERE source_id = ANY(${this.sql.array(memoryNodeIds)}::uuid[])
|
|
1245
|
+
OR target_id = ANY(${this.sql.array(memoryNodeIds)}::uuid[])
|
|
1246
|
+
ORDER BY created_at ASC, id ASC
|
|
1247
|
+
`;
|
|
1248
|
+
return rows.map((row) => this.rowToMemoryEdge(row));
|
|
1249
|
+
}
|
|
1250
|
+
buildMemoryHistoryResult(memories, edges, metadata) {
|
|
1251
|
+
const memoriesById = new Map(memories.map((memory) => [memory.id, memory]));
|
|
1252
|
+
const visibleEdges = edges.filter((edge) => memoriesById.has(edge.sourceId) && memoriesById.has(edge.targetId));
|
|
1253
|
+
const sortedMemories = [...memories].sort((left, right) => left.createdAt.getTime() - right.createdAt.getTime()
|
|
1254
|
+
|| left.id.localeCompare(right.id));
|
|
1255
|
+
const entries = sortedMemories.map((memory, index) => {
|
|
1256
|
+
const updateEdges = visibleEdges.filter((edge) => edge.edgeType === "updates"
|
|
1257
|
+
&& (edge.sourceId === memory.id || edge.targetId === memory.id));
|
|
1258
|
+
const conflictEdges = visibleEdges.filter((edge) => edge.edgeType === "conflicts"
|
|
1259
|
+
&& (edge.sourceId === memory.id || edge.targetId === memory.id));
|
|
1260
|
+
const supersedes = [
|
|
1261
|
+
...sortedMemories
|
|
1262
|
+
.filter((candidate) => candidate.supersededBy === memory.id)
|
|
1263
|
+
.map((candidate) => candidate.id),
|
|
1264
|
+
...updateEdges
|
|
1265
|
+
.filter((edge) => edge.sourceId === memory.id)
|
|
1266
|
+
.map((edge) => edge.targetId),
|
|
1267
|
+
];
|
|
1268
|
+
const conflictsWith = conflictEdges.map((edge) => edge.sourceId === memory.id ? edge.targetId : edge.sourceId);
|
|
1269
|
+
return {
|
|
1270
|
+
memory,
|
|
1271
|
+
versionIndex: index,
|
|
1272
|
+
status: this.resolveMemoryHistoryStatus(memory, conflictEdges),
|
|
1273
|
+
supersedes: [...new Set(supersedes)],
|
|
1274
|
+
supersededBy: memory.supersededBy,
|
|
1275
|
+
conflictsWith: [...new Set(conflictsWith)],
|
|
1276
|
+
updateEdges,
|
|
1277
|
+
conflictEdges,
|
|
1278
|
+
createdAt: memory.createdAt,
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1281
|
+
return {
|
|
1282
|
+
...metadata,
|
|
1283
|
+
entries,
|
|
1284
|
+
edges: visibleEdges,
|
|
1285
|
+
currentMemory: this.resolveCurrentMemory(sortedMemories),
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
resolveMemoryHistoryStatus(memory, conflictEdges) {
|
|
1289
|
+
if (memory.forgotten)
|
|
1290
|
+
return "forgotten";
|
|
1291
|
+
if (memory.decayed)
|
|
1292
|
+
return "decayed";
|
|
1293
|
+
if (memory.supersededBy != null)
|
|
1294
|
+
return "superseded";
|
|
1295
|
+
if (memory.hasConflict || conflictEdges.length > 0)
|
|
1296
|
+
return "conflicting";
|
|
1297
|
+
return "active";
|
|
1298
|
+
}
|
|
1299
|
+
resolveCurrentMemory(memories) {
|
|
1300
|
+
return [...memories]
|
|
1301
|
+
.filter((memory) => !memory.forgotten && !memory.decayed && memory.supersededBy == null)
|
|
1302
|
+
.sort((left, right) => right.createdAt.getTime() - left.createdAt.getTime()
|
|
1303
|
+
|| right.id.localeCompare(left.id))[0] ?? null;
|
|
1304
|
+
}
|
|
1305
|
+
getConnectedMemoryIds(edges) {
|
|
1306
|
+
return [...new Set(edges.flatMap((edge) => [edge.sourceId, edge.targetId]))];
|
|
1307
|
+
}
|
|
1308
|
+
diffMemoryFields(from, to) {
|
|
1309
|
+
const fields = [
|
|
1310
|
+
"sessionId",
|
|
1311
|
+
"topicNodeId",
|
|
1312
|
+
"agentId",
|
|
1313
|
+
"memoryType",
|
|
1314
|
+
"sourceType",
|
|
1315
|
+
"subject",
|
|
1316
|
+
"predicate",
|
|
1317
|
+
"value",
|
|
1318
|
+
"confidence",
|
|
1319
|
+
"tags",
|
|
1320
|
+
"source",
|
|
1321
|
+
"sourceUrl",
|
|
1322
|
+
"sourceTitle",
|
|
1323
|
+
"supersededBy",
|
|
1324
|
+
"decayed",
|
|
1325
|
+
"forgotten",
|
|
1326
|
+
"forgottenAt",
|
|
1327
|
+
"hasConflict",
|
|
1328
|
+
"agentColor",
|
|
1329
|
+
"fleetId",
|
|
1330
|
+
"createdAt",
|
|
1331
|
+
];
|
|
1332
|
+
return fields.map((field) => ({
|
|
1333
|
+
field,
|
|
1334
|
+
from: from[field],
|
|
1335
|
+
to: to[field],
|
|
1336
|
+
changed: !this.memoryDiffValuesEqual(from[field], to[field]),
|
|
1337
|
+
}));
|
|
1338
|
+
}
|
|
1339
|
+
memoryDiffValuesEqual(left, right) {
|
|
1340
|
+
if (left instanceof Date && right instanceof Date) {
|
|
1341
|
+
return left.getTime() === right.getTime();
|
|
1342
|
+
}
|
|
1343
|
+
return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
|
|
1344
|
+
}
|
|
1345
|
+
normalizeMemoryHistoryPart(value) {
|
|
1346
|
+
return value.trim().toLowerCase().replace(/\s+/g, " ");
|
|
1347
|
+
}
|
|
924
1348
|
async deleteEdgesByNodeIds(nodeIds) {
|
|
925
1349
|
if (nodeIds.length === 0)
|
|
926
1350
|
return;
|
|
@@ -974,6 +1398,38 @@ export class PostgresGraphStore {
|
|
|
974
1398
|
async close() {
|
|
975
1399
|
await this.sql.end();
|
|
976
1400
|
}
|
|
1401
|
+
async getExistingExtensions(extensionNames) {
|
|
1402
|
+
if (extensionNames.length === 0)
|
|
1403
|
+
return new Set();
|
|
1404
|
+
const rows = await this.sql `
|
|
1405
|
+
SELECT extname
|
|
1406
|
+
FROM pg_extension
|
|
1407
|
+
`;
|
|
1408
|
+
const expected = new Set(extensionNames);
|
|
1409
|
+
return new Set(rows.map((row) => row.extname).filter((name) => expected.has(name)));
|
|
1410
|
+
}
|
|
1411
|
+
async getExistingTables(tableNames) {
|
|
1412
|
+
if (tableNames.length === 0)
|
|
1413
|
+
return new Set();
|
|
1414
|
+
const rows = await this.sql `
|
|
1415
|
+
SELECT table_name
|
|
1416
|
+
FROM information_schema.tables
|
|
1417
|
+
WHERE table_schema = 'public'
|
|
1418
|
+
`;
|
|
1419
|
+
const expected = new Set(tableNames);
|
|
1420
|
+
return new Set(rows.map((row) => row.table_name).filter((name) => expected.has(name)));
|
|
1421
|
+
}
|
|
1422
|
+
async getExistingIndexes(indexNames) {
|
|
1423
|
+
if (indexNames.length === 0)
|
|
1424
|
+
return new Set();
|
|
1425
|
+
const rows = await this.sql `
|
|
1426
|
+
SELECT indexname
|
|
1427
|
+
FROM pg_indexes
|
|
1428
|
+
WHERE schemaname = 'public'
|
|
1429
|
+
`;
|
|
1430
|
+
const expected = new Set(indexNames);
|
|
1431
|
+
return new Set(rows.map((row) => row.indexname).filter((name) => expected.has(name)));
|
|
1432
|
+
}
|
|
977
1433
|
async migrateExistingNodeTable() {
|
|
978
1434
|
await this.sql `ALTER TABLE mg_segments DROP COLUMN IF EXISTS node_id`;
|
|
979
1435
|
await this.sql `ALTER TABLE mg_topic_nodes ADD COLUMN IF NOT EXISTS segment_id TEXT`;
|
|
@@ -1016,6 +1472,11 @@ export class PostgresGraphStore {
|
|
|
1016
1472
|
await this.sql `
|
|
1017
1473
|
CREATE INDEX IF NOT EXISTS mg_nodes_session_idx
|
|
1018
1474
|
ON mg_topic_nodes(session_id, topic_order)
|
|
1475
|
+
`;
|
|
1476
|
+
await this.sql `
|
|
1477
|
+
CREATE INDEX IF NOT EXISTS idx_topic_nodes_active_lifecycle
|
|
1478
|
+
ON mg_topic_nodes(session_id, suppressed)
|
|
1479
|
+
WHERE suppressed = FALSE
|
|
1019
1480
|
`;
|
|
1020
1481
|
await this.sql `
|
|
1021
1482
|
CREATE INDEX IF NOT EXISTS mg_topic_nodes_tags_idx
|
|
@@ -1058,6 +1519,11 @@ export class PostgresGraphStore {
|
|
|
1058
1519
|
await this.sql `
|
|
1059
1520
|
CREATE INDEX IF NOT EXISTS idx_memory_nodes_session
|
|
1060
1521
|
ON mg_memory_nodes(session_id)
|
|
1522
|
+
`;
|
|
1523
|
+
await this.sql `
|
|
1524
|
+
CREATE INDEX IF NOT EXISTS idx_memory_nodes_active_lifecycle
|
|
1525
|
+
ON mg_memory_nodes(session_id, forgotten, decayed)
|
|
1526
|
+
WHERE forgotten = FALSE
|
|
1061
1527
|
`;
|
|
1062
1528
|
await this.sql `
|
|
1063
1529
|
CREATE INDEX IF NOT EXISTS idx_memory_nodes_tags
|
|
@@ -1096,9 +1562,14 @@ export class PostgresGraphStore {
|
|
|
1096
1562
|
? this.sql `AND tags && ${this.sql.array(tags)}::text[]`
|
|
1097
1563
|
: this.sql `AND tags @> ${this.sql.array(tags)}::text[]`;
|
|
1098
1564
|
}
|
|
1099
|
-
memoryTagsFilterSql(tags, tagMode = "all") {
|
|
1565
|
+
memoryTagsFilterSql(tags, tagMode = "all", tableAlias) {
|
|
1100
1566
|
if (tags.length === 0)
|
|
1101
1567
|
return this.sql ``;
|
|
1568
|
+
if (tableAlias === "memory") {
|
|
1569
|
+
return tagMode === "any"
|
|
1570
|
+
? this.sql `AND memory.tags && ${this.sql.array(tags)}::text[]`
|
|
1571
|
+
: this.sql `AND memory.tags @> ${this.sql.array(tags)}::text[]`;
|
|
1572
|
+
}
|
|
1102
1573
|
return tagMode === "any"
|
|
1103
1574
|
? this.sql `AND tags && ${this.sql.array(tags)}::text[]`
|
|
1104
1575
|
: this.sql `AND tags @> ${this.sql.array(tags)}::text[]`;
|
|
@@ -1142,6 +1613,8 @@ export class PostgresGraphStore {
|
|
|
1142
1613
|
agentColor: row.agent_color,
|
|
1143
1614
|
fleetId: row.fleet_id,
|
|
1144
1615
|
agentId: row.agent_id,
|
|
1616
|
+
suppressed: row.suppressed ?? false,
|
|
1617
|
+
suppressedAt: row.suppressed_at,
|
|
1145
1618
|
createdAt: row.created_at,
|
|
1146
1619
|
};
|
|
1147
1620
|
}
|
|
@@ -1165,6 +1638,8 @@ export class PostgresGraphStore {
|
|
|
1165
1638
|
sourceTitle: row.source_title,
|
|
1166
1639
|
supersededBy: row.superseded_by,
|
|
1167
1640
|
decayed: row.decayed,
|
|
1641
|
+
forgotten: row.forgotten ?? false,
|
|
1642
|
+
forgottenAt: row.forgotten_at,
|
|
1168
1643
|
hasConflict: row.has_conflict ?? false,
|
|
1169
1644
|
agentColor: row.agent_color,
|
|
1170
1645
|
fleetId: row.fleet_id,
|