openclaw-cortex-memory 0.1.0-Alpha.30 → 0.1.0-Alpha.31
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/LICENSE +21 -0
- package/README.md +40 -6
- package/SIGNATURE.md +7 -0
- package/SKILL.md +18 -3
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +148 -6
- package/dist/index.js.map +1 -1
- package/dist/openclaw.plugin.json +120 -4
- package/dist/src/engine/memory_engine.d.ts +5 -1
- package/dist/src/engine/memory_engine.d.ts.map +1 -1
- package/dist/src/engine/ts_engine.d.ts +116 -0
- package/dist/src/engine/ts_engine.d.ts.map +1 -1
- package/dist/src/engine/ts_engine.js +417 -102
- package/dist/src/engine/ts_engine.js.map +1 -1
- package/dist/src/engine/types.d.ts +17 -0
- package/dist/src/engine/types.d.ts.map +1 -1
- package/dist/src/graph/ontology.d.ts +23 -1
- package/dist/src/graph/ontology.d.ts.map +1 -1
- package/dist/src/graph/ontology.js +743 -70
- package/dist/src/graph/ontology.js.map +1 -1
- package/dist/src/quality/llm_output_validator.d.ts +20 -2
- package/dist/src/quality/llm_output_validator.d.ts.map +1 -1
- package/dist/src/quality/llm_output_validator.js +296 -41
- package/dist/src/quality/llm_output_validator.js.map +1 -1
- package/dist/src/store/archive_store.d.ts +8 -0
- package/dist/src/store/archive_store.d.ts.map +1 -1
- package/dist/src/store/archive_store.js +244 -84
- package/dist/src/store/archive_store.js.map +1 -1
- package/dist/src/store/graph_memory_store.d.ts +72 -2
- package/dist/src/store/graph_memory_store.d.ts.map +1 -1
- package/dist/src/store/graph_memory_store.js +723 -50
- package/dist/src/store/graph_memory_store.js.map +1 -1
- package/dist/src/store/read_store.d.ts +3 -0
- package/dist/src/store/read_store.d.ts.map +1 -1
- package/dist/src/store/read_store.js +1004 -209
- package/dist/src/store/read_store.js.map +1 -1
- package/dist/src/store/vector_store.d.ts +1 -0
- package/dist/src/store/vector_store.d.ts.map +1 -1
- package/dist/src/store/vector_store.js +1 -0
- package/dist/src/store/vector_store.js.map +1 -1
- package/dist/src/store/write_store.d.ts +2 -0
- package/dist/src/store/write_store.d.ts.map +1 -1
- package/dist/src/store/write_store.js +45 -3
- package/dist/src/store/write_store.js.map +1 -1
- package/dist/src/sync/session_sync.d.ts +20 -1
- package/dist/src/sync/session_sync.d.ts.map +1 -1
- package/dist/src/sync/session_sync.js +1810 -161
- package/dist/src/sync/session_sync.js.map +1 -1
- package/dist/src/wiki/wiki_linter.d.ts +25 -0
- package/dist/src/wiki/wiki_linter.d.ts.map +1 -0
- package/dist/src/wiki/wiki_linter.js +268 -0
- package/dist/src/wiki/wiki_linter.js.map +1 -0
- package/dist/src/wiki/wiki_logger.d.ts +10 -0
- package/dist/src/wiki/wiki_logger.d.ts.map +1 -0
- package/dist/src/wiki/wiki_logger.js +78 -0
- package/dist/src/wiki/wiki_logger.js.map +1 -0
- package/dist/src/wiki/wiki_maintainer.d.ts +36 -0
- package/dist/src/wiki/wiki_maintainer.d.ts.map +1 -0
- package/dist/src/wiki/wiki_maintainer.js +38 -0
- package/dist/src/wiki/wiki_maintainer.js.map +1 -0
- package/dist/src/wiki/wiki_projector.d.ts +33 -0
- package/dist/src/wiki/wiki_projector.d.ts.map +1 -0
- package/dist/src/wiki/wiki_projector.js +633 -0
- package/dist/src/wiki/wiki_projector.js.map +1 -0
- package/dist/src/wiki/wiki_queue.d.ts +29 -0
- package/dist/src/wiki/wiki_queue.d.ts.map +1 -0
- package/dist/src/wiki/wiki_queue.js +137 -0
- package/dist/src/wiki/wiki_queue.js.map +1 -0
- package/openclaw.plugin.json +120 -4
- package/package.json +7 -2
- package/schema/graph.schema.yaml +188 -33
- package/scripts/install.js +27 -0
- package/skills/cortex-memory/SKILL.md +49 -0
- package/skills/cortex-memory/references/agent-manual.md +115 -0
- package/skills/cortex-memory/references/configuration.md +92 -0
- package/skills/cortex-memory/references/publish-checklist.md +46 -0
- package/skills/cortex-memory/references/system-prompt-template.md +27 -0
- package/skills/cortex-memory/references/tools.md +181 -0
- package/skills/cortex-memory/scripts/smoke-check.ps1 +56 -0
|
@@ -39,8 +39,10 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const ontology_1 = require("../graph/ontology");
|
|
40
40
|
const http_post_1 = require("../net/http_post");
|
|
41
41
|
const llm_output_validator_1 = require("../quality/llm_output_validator");
|
|
42
|
+
const wiki_projector_1 = require("../wiki/wiki_projector");
|
|
43
|
+
const wiki_linter_1 = require("../wiki/wiki_linter");
|
|
42
44
|
const PROMPT_VERSIONS = {
|
|
43
|
-
write_gate: "write-gate.v1.
|
|
45
|
+
write_gate: "write-gate.v1.7.9",
|
|
44
46
|
session_end_write: "session-end-write.v1.2.0",
|
|
45
47
|
read_fusion: "read-fusion.v1.2.0",
|
|
46
48
|
};
|
|
@@ -160,39 +162,12 @@ function createTsEngine(deps) {
|
|
|
160
162
|
: (typeof record.text === "string" ? record.text.trim() : "");
|
|
161
163
|
return content;
|
|
162
164
|
}
|
|
165
|
+
const sourceText = typeof record.source_text === "string" ? record.source_text.trim() : "";
|
|
166
|
+
if (sourceText) {
|
|
167
|
+
return sourceText;
|
|
168
|
+
}
|
|
163
169
|
const summary = typeof record.summary === "string" ? record.summary.trim() : "";
|
|
164
|
-
|
|
165
|
-
const outcome = typeof record.outcome === "string" ? record.outcome.trim() : "";
|
|
166
|
-
const sourceFile = typeof record.source_file === "string" ? record.source_file.trim() : "";
|
|
167
|
-
const actor = typeof record.actor === "string" ? record.actor.trim() : "";
|
|
168
|
-
const entities = Array.isArray(record.entities)
|
|
169
|
-
? record.entities.filter(v => typeof v === "string").map(v => String(v).trim()).filter(Boolean)
|
|
170
|
-
: [];
|
|
171
|
-
const relations = Array.isArray(record.relations)
|
|
172
|
-
? record.relations
|
|
173
|
-
.map(v => {
|
|
174
|
-
if (!v || typeof v !== "object")
|
|
175
|
-
return "";
|
|
176
|
-
const relation = v;
|
|
177
|
-
const source = typeof relation.source === "string" ? relation.source.trim() : "";
|
|
178
|
-
const target = typeof relation.target === "string" ? relation.target.trim() : "";
|
|
179
|
-
const type = typeof relation.type === "string" ? relation.type.trim() : "related_to";
|
|
180
|
-
if (!source || !target)
|
|
181
|
-
return "";
|
|
182
|
-
return `${source} -[${type}]-> ${target}`;
|
|
183
|
-
})
|
|
184
|
-
.filter(Boolean)
|
|
185
|
-
: [];
|
|
186
|
-
const lines = [
|
|
187
|
-
`event_type: ${eventType}`,
|
|
188
|
-
`summary: ${summary}`,
|
|
189
|
-
`outcome: ${outcome}`,
|
|
190
|
-
`entities: ${entities.join(", ")}`,
|
|
191
|
-
`source_file: ${sourceFile}`,
|
|
192
|
-
`actor: ${actor}`,
|
|
193
|
-
relations.length > 0 ? `relations: ${relations.join(" ; ")}` : "",
|
|
194
|
-
].filter(Boolean);
|
|
195
|
-
return lines.join("\n").trim();
|
|
170
|
+
return summary;
|
|
196
171
|
}
|
|
197
172
|
function splitTextChunks(text, chunkSize, chunkOverlap) {
|
|
198
173
|
const normalizedSize = Number.isFinite(chunkSize) && chunkSize >= 200 ? Math.floor(chunkSize) : 600;
|
|
@@ -245,6 +220,32 @@ function createTsEngine(deps) {
|
|
|
245
220
|
}
|
|
246
221
|
return output;
|
|
247
222
|
}
|
|
223
|
+
function pickEvidenceChunks(chunks, maxCount) {
|
|
224
|
+
if (!chunks.length || maxCount <= 0)
|
|
225
|
+
return [];
|
|
226
|
+
if (chunks.length <= maxCount)
|
|
227
|
+
return chunks;
|
|
228
|
+
const picked = new Map();
|
|
229
|
+
picked.set(chunks[0].index, chunks[0]);
|
|
230
|
+
if (maxCount >= 2) {
|
|
231
|
+
const mid = chunks[Math.floor(chunks.length / 2)];
|
|
232
|
+
picked.set(mid.index, mid);
|
|
233
|
+
}
|
|
234
|
+
if (maxCount >= 3) {
|
|
235
|
+
const last = chunks[chunks.length - 1];
|
|
236
|
+
picked.set(last.index, last);
|
|
237
|
+
}
|
|
238
|
+
if (picked.size < maxCount) {
|
|
239
|
+
for (const chunk of chunks) {
|
|
240
|
+
if (!picked.has(chunk.index)) {
|
|
241
|
+
picked.set(chunk.index, chunk);
|
|
242
|
+
}
|
|
243
|
+
if (picked.size >= maxCount)
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return [...picked.values()].sort((a, b) => a.index - b.index).slice(0, maxCount);
|
|
248
|
+
}
|
|
248
249
|
function upsertJsonFile(filePath, patch) {
|
|
249
250
|
const current = parseJsonFile(filePath) || {};
|
|
250
251
|
const next = { ...current, ...patch };
|
|
@@ -468,6 +469,27 @@ function createTsEngine(deps) {
|
|
|
468
469
|
: typeof rawArgs.event?.outcome === "string"
|
|
469
470
|
? String(rawArgs.event.outcome)
|
|
470
471
|
: "";
|
|
472
|
+
const causeValue = typeof rawArgs.cause === "string"
|
|
473
|
+
? rawArgs.cause
|
|
474
|
+
: typeof rawArgs.input?.cause === "string"
|
|
475
|
+
? String(rawArgs.input.cause)
|
|
476
|
+
: typeof rawArgs.event?.cause === "string"
|
|
477
|
+
? String(rawArgs.event.cause)
|
|
478
|
+
: "";
|
|
479
|
+
const processValue = typeof rawArgs.process === "string"
|
|
480
|
+
? rawArgs.process
|
|
481
|
+
: typeof rawArgs.input?.process === "string"
|
|
482
|
+
? String(rawArgs.input.process)
|
|
483
|
+
: typeof rawArgs.event?.process === "string"
|
|
484
|
+
? String(rawArgs.event.process)
|
|
485
|
+
: "";
|
|
486
|
+
const resultValue = typeof rawArgs.result === "string"
|
|
487
|
+
? rawArgs.result
|
|
488
|
+
: typeof rawArgs.input?.result === "string"
|
|
489
|
+
? String(rawArgs.input.result)
|
|
490
|
+
: typeof rawArgs.event?.result === "string"
|
|
491
|
+
? String(rawArgs.event.result)
|
|
492
|
+
: "";
|
|
471
493
|
const entityTypesInput = typeof rawArgs.entity_types === "object" && rawArgs.entity_types !== null
|
|
472
494
|
? rawArgs.entity_types
|
|
473
495
|
: typeof rawArgs.input?.entity_types === "object"
|
|
@@ -490,6 +512,9 @@ function createTsEngine(deps) {
|
|
|
490
512
|
{
|
|
491
513
|
event_type: "manual_event",
|
|
492
514
|
summary: normalizedSummary,
|
|
515
|
+
cause: causeValue.trim() || normalizedSummary,
|
|
516
|
+
process: processValue.trim() || normalizedSummary,
|
|
517
|
+
result: resultValue.trim() || outcomeValue.trim() || normalizedSummary,
|
|
493
518
|
entities,
|
|
494
519
|
relations,
|
|
495
520
|
entity_types: entityTypes,
|
|
@@ -506,12 +531,23 @@ function createTsEngine(deps) {
|
|
|
506
531
|
}
|
|
507
532
|
const storedId = result.stored[0].id;
|
|
508
533
|
if (deps.graphMemoryStore && entities.length > 0 && Object.keys(entityTypes).length > 0 && relations.length > 0) {
|
|
534
|
+
const graphSummary = normalizedSummary.toLowerCase().includes("entities:")
|
|
535
|
+
? normalizedSummary
|
|
536
|
+
: `${normalizedSummary} Entities: ${entities.join(", ")}.`;
|
|
509
537
|
const graphResult = await deps.graphMemoryStore.append({
|
|
510
538
|
sourceEventId: storedId,
|
|
511
539
|
sourceLayer: "archive_event",
|
|
512
540
|
archiveEventId: storedId,
|
|
513
541
|
sessionId: "manual",
|
|
514
542
|
sourceFile: "ts_store_event",
|
|
543
|
+
summary: graphSummary,
|
|
544
|
+
source_text_nav: {
|
|
545
|
+
layer: "archive_event",
|
|
546
|
+
session_id: "manual",
|
|
547
|
+
source_file: "ts_store_event",
|
|
548
|
+
source_memory_id: storedId,
|
|
549
|
+
source_event_id: storedId,
|
|
550
|
+
},
|
|
515
551
|
eventType: "manual_event",
|
|
516
552
|
entities,
|
|
517
553
|
entity_types: entityTypes,
|
|
@@ -544,30 +580,19 @@ function createTsEngine(deps) {
|
|
|
544
580
|
const pathTo = typeof args.path_to === "string" && args.path_to.trim() ? args.path_to.trim() : "";
|
|
545
581
|
const maxDepth = Math.max(2, Math.min(4, typeof args.max_depth === "number" ? Math.floor(args.max_depth) : 3));
|
|
546
582
|
const graphMemoryPath = path.join(deps.memoryRoot, "graph", "memory.jsonl");
|
|
583
|
+
const projectionIndexPath = path.join(deps.memoryRoot, "wiki", ".projection_index.json");
|
|
547
584
|
const nodes = new Map();
|
|
548
585
|
const edges = [];
|
|
549
|
-
const adjacency = new Map();
|
|
550
586
|
const pathAdjacency = new Map();
|
|
551
587
|
const relationTypeDistribution = new Map();
|
|
552
588
|
const edgeKeySet = new Set();
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
edgeKeySet.add(key);
|
|
559
|
-
edges.push({ source, target, type });
|
|
560
|
-
relationTypeDistribution.set(type, (relationTypeDistribution.get(type) || 0) + 1);
|
|
561
|
-
if (!adjacency.has(source)) {
|
|
562
|
-
adjacency.set(source, []);
|
|
563
|
-
}
|
|
564
|
-
adjacency.get(source)?.push({ next: target, edge: { source, target, type } });
|
|
565
|
-
if (!adjacency.has(target)) {
|
|
566
|
-
adjacency.set(target, []);
|
|
567
|
-
}
|
|
568
|
-
adjacency.get(target)?.push({ next: source, edge: { source, target, type } });
|
|
589
|
+
const allEdges = [];
|
|
590
|
+
function relationKeyOf(source, type, target) {
|
|
591
|
+
return `${source.trim().toLowerCase()}|${type.trim().toLowerCase()}|${target.trim().toLowerCase()}`;
|
|
569
592
|
}
|
|
570
|
-
function pushPathEdge(
|
|
593
|
+
function pushPathEdge(edge) {
|
|
594
|
+
const source = edge.source;
|
|
595
|
+
const target = edge.target;
|
|
571
596
|
if (!pathAdjacency.has(source)) {
|
|
572
597
|
pathAdjacency.set(source, []);
|
|
573
598
|
}
|
|
@@ -575,69 +600,170 @@ function createTsEngine(deps) {
|
|
|
575
600
|
pathAdjacency.set(target, []);
|
|
576
601
|
}
|
|
577
602
|
if (direction === "incoming") {
|
|
578
|
-
pathAdjacency.get(target)?.push({ next: source, edge
|
|
603
|
+
pathAdjacency.get(target)?.push({ next: source, edge });
|
|
579
604
|
}
|
|
580
605
|
else if (direction === "outgoing") {
|
|
581
|
-
pathAdjacency.get(source)?.push({ next: target, edge
|
|
606
|
+
pathAdjacency.get(source)?.push({ next: target, edge });
|
|
582
607
|
}
|
|
583
608
|
else {
|
|
584
|
-
pathAdjacency.get(source)?.push({ next: target, edge
|
|
585
|
-
pathAdjacency.get(target)?.push({ next: source, edge
|
|
609
|
+
pathAdjacency.get(source)?.push({ next: target, edge });
|
|
610
|
+
pathAdjacency.get(target)?.push({ next: source, edge });
|
|
586
611
|
}
|
|
587
612
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (relFilter && type !== relFilter) {
|
|
613
|
+
function normalizeStatus(value) {
|
|
614
|
+
const token = (value || "").trim().toLowerCase();
|
|
615
|
+
if (token === "pending" || token === "pending_conflict")
|
|
616
|
+
return "pending_conflict";
|
|
617
|
+
if (token === "superseded")
|
|
618
|
+
return "superseded";
|
|
619
|
+
if (token === "rejected")
|
|
620
|
+
return "rejected";
|
|
621
|
+
return "active";
|
|
622
|
+
}
|
|
623
|
+
const viewData = deps.graphMemoryStore?.exportGraphView();
|
|
624
|
+
if (viewData && Array.isArray(viewData.edges)) {
|
|
625
|
+
for (const edge of viewData.edges) {
|
|
626
|
+
const source = typeof edge.source === "string" ? edge.source.trim() : "";
|
|
627
|
+
const target = typeof edge.target === "string" ? edge.target.trim() : "";
|
|
628
|
+
const type = (0, ontology_1.normalizeRelationType)(typeof edge.type === "string" ? edge.type : "related_to", graphSchema);
|
|
629
|
+
if (!source || !target)
|
|
606
630
|
continue;
|
|
631
|
+
const relationKey = typeof edge.relation_key === "string" && edge.relation_key.trim()
|
|
632
|
+
? edge.relation_key.trim().toLowerCase()
|
|
633
|
+
: relationKeyOf(source, type, target);
|
|
634
|
+
allEdges.push({
|
|
635
|
+
source,
|
|
636
|
+
target,
|
|
637
|
+
type,
|
|
638
|
+
fact_status: normalizeStatus(typeof edge.status === "string" ? edge.status : "active"),
|
|
639
|
+
relation_key: relationKey,
|
|
640
|
+
source_event_id: typeof edge.source_event_id === "string" ? edge.source_event_id : undefined,
|
|
641
|
+
evidence_span: typeof edge.evidence_span === "string" ? edge.evidence_span : undefined,
|
|
642
|
+
confidence: typeof edge.confidence === "number" ? edge.confidence : undefined,
|
|
643
|
+
conflict_id: typeof edge.conflict_id === "string" ? edge.conflict_id : undefined,
|
|
644
|
+
evidence_ids: [],
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (allEdges.length === 0) {
|
|
649
|
+
const graphRecords = deps.graphMemoryStore
|
|
650
|
+
? deps.graphMemoryStore.loadAll()
|
|
651
|
+
: (fs.existsSync(graphMemoryPath) ? readJsonl(graphMemoryPath) : []);
|
|
652
|
+
for (const record of graphRecords) {
|
|
653
|
+
const sourceEventId = typeof record.source_event_id === "string" ? record.source_event_id.trim() : "";
|
|
654
|
+
const relations = Array.isArray(record.relations) ? record.relations : [];
|
|
655
|
+
for (const relationRaw of relations) {
|
|
656
|
+
if (typeof relationRaw !== "object" || relationRaw === null)
|
|
657
|
+
continue;
|
|
658
|
+
const relation = relationRaw;
|
|
659
|
+
const source = typeof relation.source === "string" ? relation.source.trim() : "";
|
|
660
|
+
const target = typeof relation.target === "string" ? relation.target.trim() : "";
|
|
661
|
+
const type = (0, ontology_1.normalizeRelationType)(typeof relation.type === "string" && relation.type.trim() ? relation.type.trim() : "related_to", graphSchema);
|
|
662
|
+
if (!source || !target)
|
|
663
|
+
continue;
|
|
664
|
+
allEdges.push({
|
|
665
|
+
source,
|
|
666
|
+
target,
|
|
667
|
+
type,
|
|
668
|
+
fact_status: "active",
|
|
669
|
+
relation_key: relationKeyOf(source, type, target),
|
|
670
|
+
source_event_id: sourceEventId || undefined,
|
|
671
|
+
evidence_span: typeof relation.evidence_span === "string" ? relation.evidence_span.trim() : undefined,
|
|
672
|
+
confidence: typeof relation.confidence === "number" ? relation.confidence : undefined,
|
|
673
|
+
evidence_ids: [],
|
|
674
|
+
});
|
|
607
675
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const projectionIndex = parseJsonFile(projectionIndexPath);
|
|
679
|
+
const entityWikiPathByName = new Map();
|
|
680
|
+
const topicWikiPathByType = new Map();
|
|
681
|
+
const projectionEntities = Array.isArray(projectionIndex?.entities) ? projectionIndex.entities : [];
|
|
682
|
+
const projectionTopics = Array.isArray(projectionIndex?.topics) ? projectionIndex.topics : [];
|
|
683
|
+
for (const item of projectionEntities) {
|
|
684
|
+
if (!item || typeof item !== "object")
|
|
685
|
+
continue;
|
|
686
|
+
const row = item;
|
|
687
|
+
const name = typeof row.name === "string" ? row.name.trim() : "";
|
|
688
|
+
const wikiPath = typeof row.path === "string" ? row.path.trim() : "";
|
|
689
|
+
if (!name || !wikiPath)
|
|
690
|
+
continue;
|
|
691
|
+
entityWikiPathByName.set(name.toLowerCase(), wikiPath);
|
|
692
|
+
}
|
|
693
|
+
for (const item of projectionTopics) {
|
|
694
|
+
if (!item || typeof item !== "object")
|
|
695
|
+
continue;
|
|
696
|
+
const row = item;
|
|
697
|
+
const topic = typeof row.type === "string" ? row.type.trim() : "";
|
|
698
|
+
const wikiPath = typeof row.path === "string" ? row.path.trim() : "";
|
|
699
|
+
if (!topic || !wikiPath)
|
|
700
|
+
continue;
|
|
701
|
+
topicWikiPathByType.set(topic.toLowerCase(), wikiPath);
|
|
702
|
+
}
|
|
703
|
+
function statusAnchor(status) {
|
|
704
|
+
if (status === "active")
|
|
705
|
+
return "current-facts";
|
|
706
|
+
if (status === "pending_conflict")
|
|
707
|
+
return "disputed-facts";
|
|
708
|
+
return "history";
|
|
709
|
+
}
|
|
710
|
+
function buildEvidenceIdsForEdge(edge) {
|
|
711
|
+
const ids = [
|
|
712
|
+
`graph:relation:${edge.relation_key}`,
|
|
713
|
+
edge.source_event_id ? `graph:event:${edge.source_event_id}` : "",
|
|
714
|
+
edge.evidence_span ? `graph:evidence:${edge.source_event_id || edge.relation_key}` : "",
|
|
715
|
+
edge.conflict_id ? `graph:conflict:${edge.conflict_id}` : "",
|
|
716
|
+
];
|
|
717
|
+
const anchor = statusAnchor(edge.fact_status);
|
|
718
|
+
for (const name of [edge.source, edge.target]) {
|
|
719
|
+
const wikiPath = entityWikiPathByName.get(name.toLowerCase());
|
|
720
|
+
if (wikiPath) {
|
|
721
|
+
ids.push(`wiki:${wikiPath}#${anchor}`);
|
|
616
722
|
}
|
|
617
|
-
explicitMatched = true;
|
|
618
|
-
if (!nodes.has(source))
|
|
619
|
-
nodes.set(source, { id: source, type: "entity" });
|
|
620
|
-
if (!nodes.has(target))
|
|
621
|
-
nodes.set(target, { id: target, type: "entity" });
|
|
622
|
-
pushEdge(source, target, type);
|
|
623
723
|
}
|
|
624
|
-
|
|
724
|
+
const topicPath = topicWikiPathByType.get(edge.type.toLowerCase());
|
|
725
|
+
if (topicPath) {
|
|
726
|
+
ids.push(`wiki:${topicPath}#relations`);
|
|
727
|
+
}
|
|
728
|
+
return [...new Set(ids.filter(Boolean))];
|
|
729
|
+
}
|
|
730
|
+
let explicitMatched = false;
|
|
731
|
+
for (const edge of allEdges) {
|
|
732
|
+
if (relFilter && edge.type !== relFilter) {
|
|
625
733
|
continue;
|
|
626
734
|
}
|
|
627
|
-
|
|
735
|
+
const pathEligible = edge.fact_status === "active" || edge.fact_status === "pending_conflict";
|
|
736
|
+
if (pathEligible) {
|
|
737
|
+
pushPathEdge(edge);
|
|
738
|
+
}
|
|
739
|
+
const outgoingMatch = edge.source === entity;
|
|
740
|
+
const incomingMatch = edge.target === entity;
|
|
741
|
+
const directionMatched = direction === "both" ? (outgoingMatch || incomingMatch)
|
|
742
|
+
: direction === "outgoing" ? outgoingMatch
|
|
743
|
+
: incomingMatch;
|
|
744
|
+
if (!directionMatched) {
|
|
628
745
|
continue;
|
|
629
746
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
747
|
+
explicitMatched = true;
|
|
748
|
+
const edgeKey = `${edge.relation_key}|${edge.fact_status}|${edge.conflict_id || ""}`;
|
|
749
|
+
if (edgeKeySet.has(edgeKey)) {
|
|
750
|
+
continue;
|
|
634
751
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
752
|
+
edgeKeySet.add(edgeKey);
|
|
753
|
+
const enrichedEdge = {
|
|
754
|
+
...edge,
|
|
755
|
+
evidence_ids: buildEvidenceIdsForEdge(edge),
|
|
756
|
+
};
|
|
757
|
+
edges.push(enrichedEdge);
|
|
758
|
+
relationTypeDistribution.set(enrichedEdge.type, (relationTypeDistribution.get(enrichedEdge.type) || 0) + 1);
|
|
759
|
+
if (!nodes.has(enrichedEdge.source))
|
|
760
|
+
nodes.set(enrichedEdge.source, { id: enrichedEdge.source, type: "entity" });
|
|
761
|
+
if (!nodes.has(enrichedEdge.target))
|
|
762
|
+
nodes.set(enrichedEdge.target, { id: enrichedEdge.target, type: "entity" });
|
|
763
|
+
}
|
|
764
|
+
if (!explicitMatched) {
|
|
765
|
+
if (!nodes.has(entity)) {
|
|
766
|
+
nodes.set(entity, { id: entity, type: "entity" });
|
|
641
767
|
}
|
|
642
768
|
}
|
|
643
769
|
let graphPath = [];
|
|
@@ -671,6 +797,34 @@ function createTsEngine(deps) {
|
|
|
671
797
|
}
|
|
672
798
|
}
|
|
673
799
|
}
|
|
800
|
+
const statusCounts = {
|
|
801
|
+
active: 0,
|
|
802
|
+
pending_conflict: 0,
|
|
803
|
+
superseded: 0,
|
|
804
|
+
rejected: 0,
|
|
805
|
+
};
|
|
806
|
+
const wikiRefSet = new Set();
|
|
807
|
+
if (entityWikiPathByName.has(entity.toLowerCase())) {
|
|
808
|
+
wikiRefSet.add(`wiki/${entityWikiPathByName.get(entity.toLowerCase())}`);
|
|
809
|
+
}
|
|
810
|
+
for (const edge of edges) {
|
|
811
|
+
statusCounts[edge.fact_status] += 1;
|
|
812
|
+
for (const name of [edge.source, edge.target]) {
|
|
813
|
+
const wikiPath = entityWikiPathByName.get(name.toLowerCase());
|
|
814
|
+
if (wikiPath) {
|
|
815
|
+
wikiRefSet.add(`wiki/${wikiPath}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const topicPath = topicWikiPathByType.get(edge.type.toLowerCase());
|
|
819
|
+
if (topicPath) {
|
|
820
|
+
wikiRefSet.add(`wiki/${topicPath}`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
const evidenceIds = [...new Set(edges.flatMap(edge => edge.evidence_ids).filter(Boolean))];
|
|
824
|
+
const conflictEdges = edges.filter(edge => edge.fact_status === "pending_conflict" || edge.fact_status === "rejected");
|
|
825
|
+
const pendingCount = conflictEdges.filter(edge => edge.fact_status === "pending_conflict").length;
|
|
826
|
+
const rejectedCount = conflictEdges.filter(edge => edge.fact_status === "rejected").length;
|
|
827
|
+
const conflictIds = [...new Set(conflictEdges.map(edge => edge.conflict_id).filter((item) => !!item))];
|
|
674
828
|
return {
|
|
675
829
|
success: true,
|
|
676
830
|
data: {
|
|
@@ -679,13 +833,61 @@ function createTsEngine(deps) {
|
|
|
679
833
|
dir: direction,
|
|
680
834
|
nodes: [...nodes.values()],
|
|
681
835
|
edges,
|
|
836
|
+
status_counts: statusCounts,
|
|
682
837
|
path_to: pathTo || "",
|
|
683
838
|
max_depth: maxDepth,
|
|
684
839
|
path: graphPath,
|
|
840
|
+
wiki_refs: [...wikiRefSet],
|
|
841
|
+
evidence_ids: evidenceIds,
|
|
842
|
+
conflict_hint: conflictEdges.length > 0
|
|
843
|
+
? {
|
|
844
|
+
pending_count: pendingCount,
|
|
845
|
+
rejected_count: rejectedCount,
|
|
846
|
+
conflict_ids: conflictIds,
|
|
847
|
+
suggestion: pendingCount > 0
|
|
848
|
+
? "Pending graph conflicts found. Call list_graph_conflicts first, then resolve_graph_conflict(accept/reject)."
|
|
849
|
+
: "Rejected candidate facts exist. Submit stronger evidence if you want to update the graph.",
|
|
850
|
+
}
|
|
851
|
+
: undefined,
|
|
685
852
|
relation_type_distribution: [...relationTypeDistribution.entries()].map(([type, count]) => ({ type, count })),
|
|
686
853
|
},
|
|
687
854
|
};
|
|
688
855
|
}
|
|
856
|
+
async function exportGraphView(args, _context) {
|
|
857
|
+
if (!deps.graphMemoryStore) {
|
|
858
|
+
return { success: false, error: "Graph memory store is not available." };
|
|
859
|
+
}
|
|
860
|
+
const writeSnapshot = args.write_snapshot !== false;
|
|
861
|
+
const view = deps.graphMemoryStore.exportGraphView();
|
|
862
|
+
const projection = writeSnapshot
|
|
863
|
+
? (0, wiki_projector_1.writeGraphViewProjection)({
|
|
864
|
+
memoryRoot: deps.memoryRoot,
|
|
865
|
+
view,
|
|
866
|
+
})
|
|
867
|
+
: null;
|
|
868
|
+
return {
|
|
869
|
+
success: true,
|
|
870
|
+
data: {
|
|
871
|
+
...view,
|
|
872
|
+
snapshot_written: writeSnapshot,
|
|
873
|
+
projection,
|
|
874
|
+
},
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
async function lintMemoryWiki(_args, _context) {
|
|
878
|
+
if (!deps.graphMemoryStore) {
|
|
879
|
+
return { success: false, error: "Graph memory store is not available." };
|
|
880
|
+
}
|
|
881
|
+
const graphView = deps.graphMemoryStore.exportGraphView();
|
|
882
|
+
const report = (0, wiki_linter_1.lintMemoryWiki)({
|
|
883
|
+
memoryRoot: deps.memoryRoot,
|
|
884
|
+
graphView,
|
|
885
|
+
});
|
|
886
|
+
return {
|
|
887
|
+
success: true,
|
|
888
|
+
data: report,
|
|
889
|
+
};
|
|
890
|
+
}
|
|
689
891
|
async function deleteMemory(args, _context) {
|
|
690
892
|
const targetId = args.memory_id?.trim();
|
|
691
893
|
if (!targetId) {
|
|
@@ -814,6 +1016,16 @@ function createTsEngine(deps) {
|
|
|
814
1016
|
if (layer === "all" || layer === "archive") {
|
|
815
1017
|
targetFiles.push({ layer: "archive", filePath: archivePath });
|
|
816
1018
|
}
|
|
1019
|
+
const vectorJsonlPath = path.join(deps.memoryRoot, "vector", "lancedb_events.jsonl");
|
|
1020
|
+
const vectorJsonlRecords = readJsonl(vectorJsonlPath);
|
|
1021
|
+
const vectorSourceIndex = new Set();
|
|
1022
|
+
for (const row of vectorJsonlRecords) {
|
|
1023
|
+
const rowLayer = row.layer === "active" || row.layer === "archive" ? row.layer : "";
|
|
1024
|
+
const sourceMemoryId = typeof row.source_memory_id === "string" ? row.source_memory_id.trim() : "";
|
|
1025
|
+
if (!rowLayer || !sourceMemoryId)
|
|
1026
|
+
continue;
|
|
1027
|
+
vectorSourceIndex.add(`${rowLayer}|${sourceMemoryId}`);
|
|
1028
|
+
}
|
|
817
1029
|
const queue = [];
|
|
818
1030
|
const recordsByFile = new Map();
|
|
819
1031
|
for (const target of targetFiles) {
|
|
@@ -827,6 +1039,7 @@ function createTsEngine(deps) {
|
|
|
827
1039
|
}
|
|
828
1040
|
const status = typeof record.embedding_status === "string" ? record.embedding_status.trim() : "";
|
|
829
1041
|
const hasEmbedding = Array.isArray(record.embedding) && record.embedding.length > 0;
|
|
1042
|
+
const hasVectorRows = vectorSourceIndex.has(`${target.layer}|${id}`);
|
|
830
1043
|
if (forceRebuild) {
|
|
831
1044
|
queue.push({ layer: target.layer, filePath: target.filePath, index: i });
|
|
832
1045
|
continue;
|
|
@@ -836,7 +1049,7 @@ function createTsEngine(deps) {
|
|
|
836
1049
|
continue;
|
|
837
1050
|
}
|
|
838
1051
|
}
|
|
839
|
-
else if (status === "ok" || hasEmbedding) {
|
|
1052
|
+
else if ((status === "ok" || hasEmbedding) && hasVectorRows) {
|
|
840
1053
|
continue;
|
|
841
1054
|
}
|
|
842
1055
|
const failCountRaw = failureCountState[id];
|
|
@@ -882,7 +1095,48 @@ function createTsEngine(deps) {
|
|
|
882
1095
|
}
|
|
883
1096
|
const chunkSize = deps.vectorChunking?.chunkSize ?? 600;
|
|
884
1097
|
const chunkOverlap = deps.vectorChunking?.chunkOverlap ?? 100;
|
|
885
|
-
const
|
|
1098
|
+
const evidenceMaxChunks = typeof deps.vectorChunking?.evidenceMaxChunks === "number"
|
|
1099
|
+
? Math.max(0, Math.min(8, Math.floor(deps.vectorChunking.evidenceMaxChunks)))
|
|
1100
|
+
: 2;
|
|
1101
|
+
let chunks = [];
|
|
1102
|
+
if (item.layer === "archive") {
|
|
1103
|
+
const summaryText = typeof record.summary === "string" ? record.summary.trim() : "";
|
|
1104
|
+
const sourceText = typeof record.source_text === "string" ? record.source_text.trim() : "";
|
|
1105
|
+
const summaryChunk = summaryText
|
|
1106
|
+
? [{
|
|
1107
|
+
index: 0,
|
|
1108
|
+
start: 0,
|
|
1109
|
+
end: summaryText.length,
|
|
1110
|
+
text: summaryText,
|
|
1111
|
+
source_field: "summary",
|
|
1112
|
+
}]
|
|
1113
|
+
: [];
|
|
1114
|
+
const evidenceChunks = sourceText
|
|
1115
|
+
? pickEvidenceChunks(splitTextChunks(sourceText, chunkSize, chunkOverlap), evidenceMaxChunks)
|
|
1116
|
+
: [];
|
|
1117
|
+
chunks = [
|
|
1118
|
+
...summaryChunk,
|
|
1119
|
+
...evidenceChunks.map((chunk, idx) => ({
|
|
1120
|
+
index: idx + summaryChunk.length,
|
|
1121
|
+
start: chunk.start,
|
|
1122
|
+
end: chunk.end,
|
|
1123
|
+
text: chunk.text,
|
|
1124
|
+
source_field: "evidence",
|
|
1125
|
+
})),
|
|
1126
|
+
];
|
|
1127
|
+
if (chunks.length === 0 && text) {
|
|
1128
|
+
chunks = splitTextChunks(text, chunkSize, chunkOverlap).map(chunk => ({
|
|
1129
|
+
...chunk,
|
|
1130
|
+
source_field: "summary",
|
|
1131
|
+
}));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
chunks = splitTextChunks(text, chunkSize, chunkOverlap).map(chunk => ({
|
|
1136
|
+
...chunk,
|
|
1137
|
+
source_field: "summary",
|
|
1138
|
+
}));
|
|
1139
|
+
}
|
|
886
1140
|
if (chunks.length === 0) {
|
|
887
1141
|
record.embedding_status = "failed";
|
|
888
1142
|
failed += 1;
|
|
@@ -920,6 +1174,8 @@ function createTsEngine(deps) {
|
|
|
920
1174
|
layer: item.layer,
|
|
921
1175
|
source_memory_id: id,
|
|
922
1176
|
source_memory_canonical_id: typeof record.canonical_id === "string" ? record.canonical_id : id,
|
|
1177
|
+
source_event_id: typeof record.source_event_id === "string" ? record.source_event_id : id,
|
|
1178
|
+
source_field: chunk.source_field,
|
|
923
1179
|
outcome: typeof record.outcome === "string" ? record.outcome : "",
|
|
924
1180
|
entities: Array.isArray(record.entities) ? record.entities.filter(v => typeof v === "string") : [],
|
|
925
1181
|
relations: Array.isArray(record.relations)
|
|
@@ -1222,6 +1478,9 @@ function createTsEngine(deps) {
|
|
|
1222
1478
|
const refId = stringField(record, "source_event_id") || stringField(record, "archive_event_id");
|
|
1223
1479
|
return sourceLayer === "archive_event" && !!refId && archiveIdSet.has(refId);
|
|
1224
1480
|
}).length;
|
|
1481
|
+
const graphConflictStats = deps.graphMemoryStore
|
|
1482
|
+
? deps.graphMemoryStore.getConflictStats()
|
|
1483
|
+
: { pending: 0, accepted: 0, rejected: 0 };
|
|
1225
1484
|
const schemaIssueTotal = Object.values(activeFieldIssues).reduce((sum, n) => sum + n, 0)
|
|
1226
1485
|
+ Object.values(archiveFieldIssues).reduce((sum, n) => sum + n, 0)
|
|
1227
1486
|
+ Object.values(vectorFieldIssues).reduce((sum, n) => sum + n, 0)
|
|
@@ -1273,6 +1532,7 @@ function createTsEngine(deps) {
|
|
|
1273
1532
|
},
|
|
1274
1533
|
graph_rules: {
|
|
1275
1534
|
graph_mutation_log_exists: fs.existsSync(path.join(deps.memoryRoot, "graph", "mutation_log.jsonl")),
|
|
1535
|
+
graph_conflicts: graphConflictStats,
|
|
1276
1536
|
rules_exists: fs.existsSync(path.join(deps.memoryRoot, "CORTEX_RULES.md")),
|
|
1277
1537
|
},
|
|
1278
1538
|
},
|
|
@@ -1347,7 +1607,15 @@ function createTsEngine(deps) {
|
|
|
1347
1607
|
query,
|
|
1348
1608
|
topK: typeof topKRaw === "number" && topKRaw > 0 ? Math.floor(topKRaw) : 3,
|
|
1349
1609
|
});
|
|
1350
|
-
return {
|
|
1610
|
+
return {
|
|
1611
|
+
success: true,
|
|
1612
|
+
data: {
|
|
1613
|
+
results: result.results,
|
|
1614
|
+
vector_semantic_results: result.semantic_results,
|
|
1615
|
+
vector_keyword_results: result.keyword_results,
|
|
1616
|
+
vector_search_strategy: result.strategy,
|
|
1617
|
+
},
|
|
1618
|
+
};
|
|
1351
1619
|
}
|
|
1352
1620
|
async function getHotContext(args, _context) {
|
|
1353
1621
|
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 20;
|
|
@@ -1382,6 +1650,49 @@ function createTsEngine(deps) {
|
|
|
1382
1650
|
}
|
|
1383
1651
|
return { success: true, data: result };
|
|
1384
1652
|
}
|
|
1653
|
+
async function listGraphConflicts(args, _context) {
|
|
1654
|
+
if (!deps.graphMemoryStore) {
|
|
1655
|
+
return { success: false, error: "Graph memory store is not available." };
|
|
1656
|
+
}
|
|
1657
|
+
const status = args.status === "pending" || args.status === "accepted" || args.status === "rejected" || args.status === "all"
|
|
1658
|
+
? args.status
|
|
1659
|
+
: "pending";
|
|
1660
|
+
const limit = typeof args.limit === "number" && Number.isFinite(args.limit) && args.limit > 0
|
|
1661
|
+
? Math.min(500, Math.floor(args.limit))
|
|
1662
|
+
: 50;
|
|
1663
|
+
const items = deps.graphMemoryStore.listConflicts({ status, limit });
|
|
1664
|
+
return { success: true, data: { status, count: items.length, items } };
|
|
1665
|
+
}
|
|
1666
|
+
async function resolveGraphConflict(args, _context) {
|
|
1667
|
+
if (!deps.graphMemoryStore) {
|
|
1668
|
+
return { success: false, error: "Graph memory store is not available." };
|
|
1669
|
+
}
|
|
1670
|
+
const conflictId = (args.conflict_id || "").trim();
|
|
1671
|
+
const action = args.action === "accept" || args.action === "reject" ? args.action : null;
|
|
1672
|
+
if (!conflictId) {
|
|
1673
|
+
return { success: false, error: "Invalid input provided. Missing 'conflict_id' parameter." };
|
|
1674
|
+
}
|
|
1675
|
+
if (!action) {
|
|
1676
|
+
return { success: false, error: "Invalid input provided. 'action' must be accept or reject." };
|
|
1677
|
+
}
|
|
1678
|
+
const note = typeof args.note === "string" ? args.note.trim() : undefined;
|
|
1679
|
+
const result = await deps.graphMemoryStore.resolveConflict({
|
|
1680
|
+
conflictId,
|
|
1681
|
+
action,
|
|
1682
|
+
note,
|
|
1683
|
+
});
|
|
1684
|
+
if (!result.success) {
|
|
1685
|
+
return { success: false, error: result.reason || "resolve_graph_conflict_failed" };
|
|
1686
|
+
}
|
|
1687
|
+
return {
|
|
1688
|
+
success: true,
|
|
1689
|
+
data: {
|
|
1690
|
+
conflict_id: conflictId,
|
|
1691
|
+
action,
|
|
1692
|
+
applied_record_id: result.appliedRecordId,
|
|
1693
|
+
},
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1385
1696
|
async function syncMemory(_args, _context) {
|
|
1386
1697
|
try {
|
|
1387
1698
|
const result = await deps.sessionSync.syncMemory();
|
|
@@ -1475,6 +1786,10 @@ function createTsEngine(deps) {
|
|
|
1475
1786
|
getAutoContext,
|
|
1476
1787
|
storeEvent,
|
|
1477
1788
|
queryGraph,
|
|
1789
|
+
exportGraphView,
|
|
1790
|
+
lintMemoryWiki,
|
|
1791
|
+
listGraphConflicts,
|
|
1792
|
+
resolveGraphConflict,
|
|
1478
1793
|
reflectMemory,
|
|
1479
1794
|
syncMemory,
|
|
1480
1795
|
promoteMemory,
|