stratagate-dsh 0.2.75 → 0.2.76
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/CHANGELOG.md +6 -0
- package/dist/client.js +1 -1
- package/dist/plugin.js +561 -88
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -385,6 +385,155 @@ function rrfRank(rankings) {
|
|
|
385
385
|
// packages/core/src/graph.ts
|
|
386
386
|
var NODE_TYPES = /* @__PURE__ */ new Set(["person", "project", "organization", "tool", "place"]);
|
|
387
387
|
var STATUSES = /* @__PURE__ */ new Set(["active", "superseded", "disputed", "archived"]);
|
|
388
|
+
var GRAPH_PROVENANCE_LIMIT = 6;
|
|
389
|
+
function unique(values) {
|
|
390
|
+
return [...new Set(values)];
|
|
391
|
+
}
|
|
392
|
+
function factText(fact) {
|
|
393
|
+
return `${fact.key}: ${Array.isArray(fact.value) ? fact.value.join("\u3001") : fact.value}`;
|
|
394
|
+
}
|
|
395
|
+
function renderCurrentState(facts) {
|
|
396
|
+
return facts.map((fact) => `${factText(fact)}${fact.status === "disputed" ? " [disputed]" : ""}`).join("\n");
|
|
397
|
+
}
|
|
398
|
+
var EXPOSED_EVENT_STATUSES = /* @__PURE__ */ new Set(["active", "superseded"]);
|
|
399
|
+
function exposedSources(ids, eventById) {
|
|
400
|
+
return unique((ids ?? []).filter((id) => EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten")));
|
|
401
|
+
}
|
|
402
|
+
function metadataEntries(entries, allowedValues, eventById) {
|
|
403
|
+
const allowed = new Set(allowedValues.map((value) => normalizeSearchText(value)));
|
|
404
|
+
return (entries ?? []).flatMap((entry) => {
|
|
405
|
+
const value = typeof entry.value === "string" ? entry.value.trim() : "";
|
|
406
|
+
const sourceEventIds = exposedSources(entry.sourceEventIds, eventById);
|
|
407
|
+
return value && allowed.has(normalizeSearchText(value)) && sourceEventIds.length > 0 ? [{ value, sourceEventIds }] : [];
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function effectiveMetadata(node, eventById) {
|
|
411
|
+
if (!node.metadataProvenance) {
|
|
412
|
+
const potentialSources = unique(node.sourceEventIds ?? []);
|
|
413
|
+
const allSourcesExposed = potentialSources.length > 0 && potentialSources.every((id) => EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten"));
|
|
414
|
+
return allSourcesExposed ? { name: node.name, aliases: [...node.aliases], ...node.tags ? { tags: [...node.tags] } : {} } : { name: "", aliases: [] };
|
|
415
|
+
}
|
|
416
|
+
const nameSources = exposedSources(node.metadataProvenance.name, eventById);
|
|
417
|
+
const aliases = metadataEntries(node.metadataProvenance.aliases, node.aliases, eventById);
|
|
418
|
+
const tags = metadataEntries(node.metadataProvenance.tags, node.tags ?? [], eventById);
|
|
419
|
+
const metadataProvenance = {
|
|
420
|
+
...nameSources.length > 0 ? { name: nameSources } : {},
|
|
421
|
+
...aliases.length > 0 ? { aliases } : {},
|
|
422
|
+
...tags.length > 0 ? { tags } : {}
|
|
423
|
+
};
|
|
424
|
+
return {
|
|
425
|
+
name: nameSources.length > 0 ? node.name : "",
|
|
426
|
+
aliases: aliases.map(({ value }) => value),
|
|
427
|
+
...tags.length > 0 ? { tags: tags.map(({ value }) => value) } : {},
|
|
428
|
+
metadataProvenance
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function effectiveGraphNodeView(node, edges, events) {
|
|
432
|
+
if (node.status !== "active" && node.status !== "disputed") return null;
|
|
433
|
+
const eventById = new Map(events.map((event) => [event.id, event]));
|
|
434
|
+
const sources2 = (ids, statuses) => unique((ids ?? []).filter((id) => {
|
|
435
|
+
const event = eventById.get(id);
|
|
436
|
+
return event !== void 0 && statuses.has(event.status);
|
|
437
|
+
}));
|
|
438
|
+
const activeStatuses = /* @__PURE__ */ new Set(["active"]);
|
|
439
|
+
const visibleStatuses = /* @__PURE__ */ new Set(["active", "superseded"]);
|
|
440
|
+
const splitRecord = (record2) => {
|
|
441
|
+
if (record2.status === "archived") return {};
|
|
442
|
+
const activeSources = sources2(record2.sourceEventIds, activeStatuses);
|
|
443
|
+
const visibleSources = sources2(record2.sourceEventIds, visibleStatuses);
|
|
444
|
+
if ((record2.status === "active" || record2.status === "disputed") && activeSources.length > 0) {
|
|
445
|
+
return { current: { ...record2, sourceEventIds: activeSources } };
|
|
446
|
+
}
|
|
447
|
+
if (visibleSources.length > 0 && record2.status === "superseded") {
|
|
448
|
+
return { historical: { ...record2, sourceEventIds: visibleSources } };
|
|
449
|
+
}
|
|
450
|
+
return {};
|
|
451
|
+
};
|
|
452
|
+
const currentFacts = [];
|
|
453
|
+
const historicalFacts = [];
|
|
454
|
+
for (const fact of node.facts ?? []) {
|
|
455
|
+
const split = splitRecord(fact);
|
|
456
|
+
if (split.current) currentFacts.push(split.current);
|
|
457
|
+
if (split.historical) historicalFacts.push(split.historical);
|
|
458
|
+
}
|
|
459
|
+
const currentEdges = [];
|
|
460
|
+
const historicalEdges = [];
|
|
461
|
+
for (const edge of edges.filter(({ fromNodeId, toNodeId }) => fromNodeId === node.id || toNodeId === node.id)) {
|
|
462
|
+
const split = splitRecord(edge);
|
|
463
|
+
if (split.current) currentEdges.push(split.current);
|
|
464
|
+
if (split.historical) historicalEdges.push(split.historical);
|
|
465
|
+
}
|
|
466
|
+
const currentNodeEventIds = sources2(node.sourceEventIds, activeStatuses);
|
|
467
|
+
const historicalNodeEventIds = sources2(node.sourceEventIds, /* @__PURE__ */ new Set(["superseded"]));
|
|
468
|
+
if (currentFacts.length === 0 && historicalFacts.length === 0 && currentEdges.length === 0 && historicalEdges.length === 0 && currentNodeEventIds.length === 0 && historicalNodeEventIds.length === 0) return null;
|
|
469
|
+
const visibleNodeSources = unique([...currentNodeEventIds, ...historicalNodeEventIds]);
|
|
470
|
+
const metadata = effectiveMetadata(node, eventById);
|
|
471
|
+
const effectiveNode = { ...node, ...metadata };
|
|
472
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, "tags")) delete effectiveNode.tags;
|
|
473
|
+
return {
|
|
474
|
+
node: { ...effectiveNode, currentState: renderCurrentState(currentFacts), facts: currentFacts, sourceEventIds: visibleNodeSources },
|
|
475
|
+
currentFacts,
|
|
476
|
+
historicalFacts,
|
|
477
|
+
currentEdges,
|
|
478
|
+
historicalEdges,
|
|
479
|
+
currentNodeEventIds,
|
|
480
|
+
historicalNodeEventIds
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function boundEffectiveGraphNodeView(view, eventIds, options = {}) {
|
|
484
|
+
const boundRecord = (record2) => {
|
|
485
|
+
const sourceEventIds = record2.sourceEventIds.filter((id) => eventIds.has(id));
|
|
486
|
+
return sourceEventIds.length > 0 ? { ...record2, sourceEventIds } : null;
|
|
487
|
+
};
|
|
488
|
+
const currentFacts = view.currentFacts.flatMap((record2) => boundRecord(record2) ?? []);
|
|
489
|
+
const historicalFacts = view.historicalFacts.flatMap((record2) => boundRecord(record2) ?? []);
|
|
490
|
+
const currentEdges = view.currentEdges.flatMap((record2) => boundRecord(record2) ?? []);
|
|
491
|
+
const historicalEdges = view.historicalEdges.flatMap((record2) => boundRecord(record2) ?? []);
|
|
492
|
+
const metadata = view.node.metadataProvenance ? (() => {
|
|
493
|
+
const name2 = (view.node.metadataProvenance?.name ?? []).filter((id) => eventIds.has(id));
|
|
494
|
+
const aliases = (view.node.metadataProvenance?.aliases ?? []).flatMap((entry) => {
|
|
495
|
+
const sourceEventIds = entry.sourceEventIds.filter((id) => eventIds.has(id));
|
|
496
|
+
return sourceEventIds.length > 0 ? [{ ...entry, sourceEventIds }] : [];
|
|
497
|
+
});
|
|
498
|
+
const tags = (view.node.metadataProvenance?.tags ?? []).flatMap((entry) => {
|
|
499
|
+
const sourceEventIds = entry.sourceEventIds.filter((id) => eventIds.has(id));
|
|
500
|
+
return sourceEventIds.length > 0 ? [{ ...entry, sourceEventIds }] : [];
|
|
501
|
+
});
|
|
502
|
+
return {
|
|
503
|
+
name: name2.length > 0 ? view.node.name : "",
|
|
504
|
+
aliases: aliases.map(({ value }) => value),
|
|
505
|
+
...tags.length > 0 ? { tags: tags.map(({ value }) => value) } : {},
|
|
506
|
+
metadataProvenance: {
|
|
507
|
+
...name2.length > 0 ? { name: name2 } : {},
|
|
508
|
+
...aliases.length > 0 ? { aliases } : {},
|
|
509
|
+
...tags.length > 0 ? { tags } : {}
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
})() : view.node.sourceEventIds.length > 0 && view.node.sourceEventIds.every((id) => eventIds.has(id)) || options.preserveLegacyMetadata ? { name: view.node.name, aliases: [...view.node.aliases], ...view.node.tags ? { tags: [...view.node.tags] } : {} } : { name: "", aliases: [] };
|
|
513
|
+
const currentNodeEventIds = view.currentNodeEventIds.filter((id) => eventIds.has(id));
|
|
514
|
+
const historicalNodeEventIds = view.historicalNodeEventIds.filter((id) => eventIds.has(id));
|
|
515
|
+
const boundedNode = { ...view.node, ...metadata };
|
|
516
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, "tags")) delete boundedNode.tags;
|
|
517
|
+
return {
|
|
518
|
+
node: { ...boundedNode, currentState: renderCurrentState(currentFacts), facts: currentFacts, sourceEventIds: unique([...currentNodeEventIds, ...historicalNodeEventIds]) },
|
|
519
|
+
currentFacts,
|
|
520
|
+
historicalFacts,
|
|
521
|
+
currentEdges,
|
|
522
|
+
historicalEdges,
|
|
523
|
+
currentNodeEventIds,
|
|
524
|
+
historicalNodeEventIds
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function graphTimeline(eventIds, events, limit = GRAPH_PROVENANCE_LIMIT) {
|
|
528
|
+
const order = new Map(eventIds.map((id, index) => [id, index]));
|
|
529
|
+
return events.filter((event) => order.has(event.id) && event.status !== "forgotten" && event.status !== "archived").sort((left, right) => (order.get(left.id) ?? 0) - (order.get(right.id) ?? 0)).slice(0, Math.max(0, limit)).map((event) => ({
|
|
530
|
+
id: event.id,
|
|
531
|
+
title: event.title,
|
|
532
|
+
summary: event.summary,
|
|
533
|
+
status: event.status,
|
|
534
|
+
...event.temporal.happenedStart ? { time: event.temporal.happenedStart } : {}
|
|
535
|
+
}));
|
|
536
|
+
}
|
|
388
537
|
function text(value, limit = 240) {
|
|
389
538
|
return typeof value === "string" ? value.trim().replace(/\s+/g, " ").slice(0, limit) : "";
|
|
390
539
|
}
|
|
@@ -397,20 +546,48 @@ function confidence(value) {
|
|
|
397
546
|
function chronology(events, ids, fallback) {
|
|
398
547
|
return ids.flatMap((id) => events.find((event) => event.id === id) ?? []).map((event) => event.temporal.happenedStart ?? event.temporal.happenedEnd ?? event.temporal.mentionedAt ?? event.createdAt).sort().at(-1) ?? fallback;
|
|
399
548
|
}
|
|
400
|
-
function
|
|
401
|
-
if (node.type !== proposal.type) return
|
|
549
|
+
function entityMatchKind(node, proposal, eventById) {
|
|
550
|
+
if (node.type !== proposal.type) return null;
|
|
402
551
|
const key = (value) => normalizeSearchText(value).replace(/[\s_-]+/g, "");
|
|
403
|
-
const
|
|
404
|
-
|
|
552
|
+
const metadata = effectiveMetadata(node, eventById);
|
|
553
|
+
const proposalNames = new Set([proposal.name, ...proposal.aliases ?? []].map(key));
|
|
554
|
+
if (proposalNames.has(key(metadata.name))) return "canonical-name";
|
|
555
|
+
if (metadata.aliases.some((value) => proposalNames.has(key(value)))) return "alias";
|
|
556
|
+
return null;
|
|
405
557
|
}
|
|
406
558
|
function applyGraphProjection(options) {
|
|
407
559
|
const refs = /* @__PURE__ */ new Map();
|
|
408
560
|
const touchedNodes = /* @__PURE__ */ new Set();
|
|
409
561
|
const touchedEdges = /* @__PURE__ */ new Set();
|
|
562
|
+
const warnings = [];
|
|
563
|
+
const eventById = new Map(options.events.map((event) => [event.id, event]));
|
|
410
564
|
const validSources = (value) => {
|
|
411
565
|
const requested = strings(value, 64);
|
|
412
|
-
return requested.length > 0 && requested.every((id) => options.allowedEventIds.has(id)) ? requested : [];
|
|
566
|
+
return requested.length > 0 && requested.every((id) => options.allowedEventIds.has(id) && EXPOSED_EVENT_STATUSES.has(eventById.get(id)?.status ?? "forgotten")) ? requested : [];
|
|
567
|
+
};
|
|
568
|
+
const activeSources = (ids) => ids.filter((id) => eventById.get(id)?.status === "active");
|
|
569
|
+
const metadataEntriesFromProjection = (entries, fallbackValues) => {
|
|
570
|
+
const byValue = new Map((entries ?? []).map((entry) => [normalizeSearchText(entry.value), entry]));
|
|
571
|
+
return fallbackValues.flatMap((value) => {
|
|
572
|
+
const entry = byValue.get(normalizeSearchText(value));
|
|
573
|
+
const sourceEventIds = validSources(entry?.sourceEventIds);
|
|
574
|
+
if (sourceEventIds.length > 0) return [{ value, sourceEventIds }];
|
|
575
|
+
warnings.push(`Dropped metadata value "${value}" because it lacks matching valid field provenance.`);
|
|
576
|
+
return [];
|
|
577
|
+
});
|
|
413
578
|
};
|
|
579
|
+
for (const proposal of Array.isArray(options.result.nodes) ? options.result.nodes : []) {
|
|
580
|
+
const ref = text(proposal.ref, 120);
|
|
581
|
+
const name2 = text(proposal.name, 160);
|
|
582
|
+
const sources2 = validSources(proposal.sourceEventIds);
|
|
583
|
+
if (!ref || !name2 || !NODE_TYPES.has(proposal.type)) continue;
|
|
584
|
+
if (sources2.length === 0) {
|
|
585
|
+
throw new Error(`Graph projection validation failed: node "${ref}" has no valid exposable source Event.`);
|
|
586
|
+
}
|
|
587
|
+
if (validSources(proposal.metadataProvenance?.name).length === 0) {
|
|
588
|
+
throw new Error(`Graph projection validation failed: node "${ref}" name "${name2}" lacks valid metadata provenance.`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
414
591
|
for (const proposal of Array.isArray(options.result.nodes) ? options.result.nodes : []) {
|
|
415
592
|
const ref = text(proposal.ref, 120);
|
|
416
593
|
const name2 = text(proposal.name, 160);
|
|
@@ -418,7 +595,15 @@ function applyGraphProjection(options) {
|
|
|
418
595
|
if (!ref || !name2 || !NODE_TYPES.has(proposal.type) || sources2.length === 0) continue;
|
|
419
596
|
const aliases = strings(proposal.aliases, 20).filter((alias) => normalizeSearchText(alias) !== normalizeSearchText(name2));
|
|
420
597
|
const tags = strings(proposal.tags, 12);
|
|
421
|
-
|
|
598
|
+
const projectedNameSources = validSources(proposal.metadataProvenance?.name);
|
|
599
|
+
if (projectedNameSources.length === 0) continue;
|
|
600
|
+
const projectedMetadata = {
|
|
601
|
+
name: projectedNameSources,
|
|
602
|
+
aliases: metadataEntriesFromProjection(proposal.metadataProvenance?.aliases, aliases),
|
|
603
|
+
tags: metadataEntriesFromProjection(proposal.metadataProvenance?.tags, tags)
|
|
604
|
+
};
|
|
605
|
+
const provenAliases = projectedMetadata.aliases.map(({ value }) => value);
|
|
606
|
+
let node = options.nodes.find((candidate) => entityMatchKind(candidate, { name: name2, aliases: provenAliases, type: proposal.type }, eventById));
|
|
422
607
|
if (!node) {
|
|
423
608
|
node = {
|
|
424
609
|
id: options.idFactory("node"),
|
|
@@ -435,8 +620,37 @@ function applyGraphProjection(options) {
|
|
|
435
620
|
};
|
|
436
621
|
options.nodes.push(node);
|
|
437
622
|
}
|
|
438
|
-
|
|
439
|
-
|
|
623
|
+
const canonicalKey = normalizeSearchText(node.name);
|
|
624
|
+
const canonicalNameSources = [
|
|
625
|
+
...normalizeSearchText(name2) === canonicalKey ? projectedMetadata.name : [],
|
|
626
|
+
...projectedMetadata.aliases.filter(({ value }) => normalizeSearchText(value) === canonicalKey).flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
627
|
+
...node.metadataProvenance?.name ?? []
|
|
628
|
+
];
|
|
629
|
+
const projectedAliasEntries = [
|
|
630
|
+
...projectedMetadata.aliases.filter(({ value }) => normalizeSearchText(value) !== canonicalKey),
|
|
631
|
+
...normalizeSearchText(name2) !== canonicalKey ? [{ value: name2, sourceEventIds: projectedMetadata.name }] : [],
|
|
632
|
+
...aliases.filter((alias) => normalizeSearchText(alias) !== canonicalKey && !projectedMetadata.aliases.some((entry) => normalizeSearchText(entry.value) === normalizeSearchText(alias))).map((value) => ({ value, sourceEventIds: projectedMetadata.aliases.find((entry) => normalizeSearchText(entry.value) === normalizeSearchText(value))?.sourceEventIds ?? [] })).filter(({ sourceEventIds }) => sourceEventIds.length > 0)
|
|
633
|
+
];
|
|
634
|
+
node.aliases = [.../* @__PURE__ */ new Set([...node.aliases, ...projectedAliasEntries.map(({ value }) => value)])];
|
|
635
|
+
if (tags.length > 0) node.tags = [.../* @__PURE__ */ new Set([...node.tags ?? [], ...projectedMetadata.tags.map(({ value }) => value)])].slice(0, 12);
|
|
636
|
+
const previousMetadata = node.metadataProvenance;
|
|
637
|
+
const aliasEntries = [...previousMetadata?.aliases ?? []];
|
|
638
|
+
for (const entry of projectedAliasEntries) {
|
|
639
|
+
const existing = aliasEntries.find((candidate) => normalizeSearchText(candidate.value) === normalizeSearchText(entry.value));
|
|
640
|
+
if (existing) existing.sourceEventIds = [.../* @__PURE__ */ new Set([...existing.sourceEventIds, ...entry.sourceEventIds])];
|
|
641
|
+
else aliasEntries.push(entry);
|
|
642
|
+
}
|
|
643
|
+
const tagEntries = [...previousMetadata?.tags ?? []];
|
|
644
|
+
for (const entry of projectedMetadata.tags) {
|
|
645
|
+
const existing = tagEntries.find((candidate) => normalizeSearchText(candidate.value) === normalizeSearchText(entry.value));
|
|
646
|
+
if (existing) existing.sourceEventIds = [.../* @__PURE__ */ new Set([...existing.sourceEventIds, ...entry.sourceEventIds])];
|
|
647
|
+
else tagEntries.push(entry);
|
|
648
|
+
}
|
|
649
|
+
node.metadataProvenance = {
|
|
650
|
+
...canonicalNameSources.length > 0 ? { name: [...new Set(canonicalNameSources)] } : previousMetadata?.name ? { name: [...previousMetadata.name] } : {},
|
|
651
|
+
...aliasEntries.length > 0 ? { aliases: aliasEntries } : {},
|
|
652
|
+
...tagEntries.length > 0 ? { tags: tagEntries } : {}
|
|
653
|
+
};
|
|
440
654
|
node.status = STATUSES.has(proposal.status ?? "active") ? proposal.status ?? "active" : "active";
|
|
441
655
|
node.confidence = confidence(proposal.confidence);
|
|
442
656
|
node.sourceEventIds = [.../* @__PURE__ */ new Set([...node.sourceEventIds, ...sources2])];
|
|
@@ -451,7 +665,14 @@ function applyGraphProjection(options) {
|
|
|
451
665
|
const value = Array.isArray(rawFact.value) ? strings(rawFact.value, 40) : text(rawFact.value, 1200);
|
|
452
666
|
if (!key || (Array.isArray(value) ? value.length === 0 : !value)) continue;
|
|
453
667
|
const factSources = "sourceEventIds" in rawFact ? validSources(rawFact.sourceEventIds) : sources2;
|
|
454
|
-
if (factSources.length === 0)
|
|
668
|
+
if (factSources.length === 0) {
|
|
669
|
+
warnings.push(`Dropped fact "${key}" because it lacks valid exposable Event provenance.`);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if ((node.status === "active" || node.status === "disputed") && activeSources(factSources).length === 0) {
|
|
673
|
+
warnings.push(`Dropped current fact "${key}" because it has no active Event provenance.`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
455
676
|
for (const old of node.facts.filter((fact2) => fact2.status === "active" && fact2.key === key)) {
|
|
456
677
|
old.status = "superseded";
|
|
457
678
|
if (!old.validTo) old.validTo = validFrom;
|
|
@@ -485,6 +706,10 @@ function applyGraphProjection(options) {
|
|
|
485
706
|
const status = STATUSES.has(proposal.status ?? "active") ? proposal.status ?? "active" : "active";
|
|
486
707
|
const validFrom = text(proposal.validFrom, 80) || chronology(options.events, sources2, options.now);
|
|
487
708
|
const validTo = text(proposal.validTo, 80) || void 0;
|
|
709
|
+
if ((status === "active" || status === "disputed") && activeSources(sources2).length === 0) {
|
|
710
|
+
warnings.push(`Dropped current edge "${relation}" because it has no active Event provenance.`);
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
488
713
|
for (const old of options.edges.filter((edge2) => edge2.status === "active" && edge2.fromNodeId === from.id && edge2.relation === relation && edge2.toNodeId !== to.id)) {
|
|
489
714
|
old.status = "superseded";
|
|
490
715
|
if (!old.validTo) old.validTo = validFrom;
|
|
@@ -517,7 +742,7 @@ function applyGraphProjection(options) {
|
|
|
517
742
|
const participantNodeIds = options.nodes.filter((node) => node.sourceEventIds.includes(event.id)).map(({ id }) => id);
|
|
518
743
|
event.temporal.participantNodeIds = [.../* @__PURE__ */ new Set([...event.temporal.participantNodeIds ?? [], ...participantNodeIds])];
|
|
519
744
|
}
|
|
520
|
-
return { nodeIds: [...touchedNodes], edgeIds: [...touchedEdges] };
|
|
745
|
+
return { nodeIds: [...touchedNodes], edgeIds: [...touchedEdges], warnings: unique(warnings) };
|
|
521
746
|
}
|
|
522
747
|
|
|
523
748
|
// packages/core/src/events.ts
|
|
@@ -3930,15 +4155,21 @@ var StrataGate = class _StrataGate {
|
|
|
3930
4155
|
event.tags.join(" "),
|
|
3931
4156
|
(event.temporal.participants ?? []).join(" ")
|
|
3932
4157
|
].join(" ")).join(" "));
|
|
3933
|
-
const
|
|
4158
|
+
const effectiveViews = this.graphNodes.flatMap((node) => effectiveGraphNodeView(node, this.graphEdges, this.events) ?? []);
|
|
4159
|
+
const effectiveNodes = effectiveViews.map(({ node }) => node);
|
|
4160
|
+
const relevantNodes = effectiveNodes.filter((node) => [node.name, ...node.aliases].some((name2) => name2 && eventText.includes(normalizeSearchText(name2)))).concat([...effectiveNodes].sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, 24));
|
|
3934
4161
|
const existingNodes = [...new Map(relevantNodes.map((node) => [node.id, node])).values()].slice(0, 32);
|
|
3935
4162
|
const nodeIds = new Set(existingNodes.map(({ id }) => id));
|
|
4163
|
+
const visibleEdgeIds = new Set(effectiveViews.flatMap(({ currentEdges, historicalEdges }) => [
|
|
4164
|
+
...currentEdges,
|
|
4165
|
+
...historicalEdges
|
|
4166
|
+
]).map(({ id }) => id));
|
|
3936
4167
|
return {
|
|
3937
4168
|
jobId: job.id,
|
|
3938
4169
|
projectorVersion: job.projectorVersion,
|
|
3939
4170
|
events: structuredClone(events),
|
|
3940
4171
|
existingNodes: structuredClone(existingNodes),
|
|
3941
|
-
existingEdges: structuredClone(this.graphEdges.filter(({ fromNodeId, toNodeId }) => nodeIds.has(fromNodeId) && nodeIds.has(toNodeId)).slice(-60))
|
|
4172
|
+
existingEdges: structuredClone(this.graphEdges.filter(({ id, fromNodeId, toNodeId }) => visibleEdgeIds.has(id) && nodeIds.has(fromNodeId) && nodeIds.has(toNodeId)).slice(-60))
|
|
3942
4173
|
};
|
|
3943
4174
|
});
|
|
3944
4175
|
}
|
|
@@ -3962,11 +4193,13 @@ var StrataGate = class _StrataGate {
|
|
|
3962
4193
|
job.status = "completed";
|
|
3963
4194
|
job.nodeIds = touched.nodeIds;
|
|
3964
4195
|
job.edgeIds = touched.edgeIds;
|
|
3965
|
-
|
|
4196
|
+
const normalizedReason = typeof result.reason === "string" ? result.reason.trim().replace(/\s+/g, " ") : "";
|
|
4197
|
+
const warning = touched.warnings.length > 0 ? `Validation: ${touched.warnings.join(" ")}` : "";
|
|
4198
|
+
job.reason = [warning, normalizedReason].filter(Boolean).join(" ").slice(0, 500) || null;
|
|
3966
4199
|
job.lastError = null;
|
|
3967
4200
|
job.nextRetryAt = null;
|
|
3968
4201
|
job.updatedAt = toUtc8Iso(this.now());
|
|
3969
|
-
return touched;
|
|
4202
|
+
return { nodeIds: touched.nodeIds, edgeIds: touched.edgeIds };
|
|
3970
4203
|
});
|
|
3971
4204
|
}
|
|
3972
4205
|
async failGraphProjection(jobId, error) {
|
|
@@ -4313,45 +4546,179 @@ var StrataGate = class _StrataGate {
|
|
|
4313
4546
|
return job;
|
|
4314
4547
|
}
|
|
4315
4548
|
async searchGraphNodes(query, limit = 8) {
|
|
4316
|
-
const candidates = this.graphNodes.filter((node) => node.status === "active" || node.status === "disputed");
|
|
4317
4549
|
const queryTokens = [...new Set(searchTokens(query))];
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
const
|
|
4337
|
-
const
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4550
|
+
if (queryTokens.length === 0) return [];
|
|
4551
|
+
const views = this.graphNodes.flatMap((node) => effectiveGraphNodeView(node, this.graphEdges, this.events) ?? []);
|
|
4552
|
+
const viewById = new Map(views.map((view) => [view.node.id, view]));
|
|
4553
|
+
const dedupe = (values) => [...new Set(values)];
|
|
4554
|
+
const factValue = (fact) => Array.isArray(fact.value) ? fact.value.join(" ") : fact.value;
|
|
4555
|
+
const endpoint = (nodeId, edge) => {
|
|
4556
|
+
const otherId = edge.fromNodeId === nodeId ? edge.toNodeId : edge.fromNodeId;
|
|
4557
|
+
const other = viewById.get(otherId)?.node;
|
|
4558
|
+
return other ? `${other.name} ${other.aliases.join(" ")}` : "";
|
|
4559
|
+
};
|
|
4560
|
+
const tokenSet = (value) => new Set(searchTokens(value));
|
|
4561
|
+
const hasAny = (value, tokens) => {
|
|
4562
|
+
const available = tokenSet(value);
|
|
4563
|
+
return tokens.some((token) => available.has(token));
|
|
4564
|
+
};
|
|
4565
|
+
const matchDetails = (node) => {
|
|
4566
|
+
const view = viewById.get(node.id);
|
|
4567
|
+
const queryParts = normalizeSearchText(query).split(/[^\p{Letter}\p{Number}_]+/gu).filter(Boolean);
|
|
4568
|
+
const explicitFactKeys = dedupe([...view.currentFacts, ...view.historicalFacts].map(({ key }) => key)).filter((key) => queryParts.includes(normalizeSearchText(key)));
|
|
4569
|
+
const knownFactKeyTokens = new Set(explicitFactKeys.flatMap((key) => [...tokenSet(key)]));
|
|
4570
|
+
const queryKeyTokens = queryTokens.filter((token) => knownFactKeyTokens.has(token));
|
|
4571
|
+
const valueQueryTokens = queryTokens.filter((token) => !queryKeyTokens.includes(token));
|
|
4572
|
+
const meaningfulTokens = queryTokens;
|
|
4573
|
+
const factMatchesFor = (facts) => {
|
|
4574
|
+
if (queryKeyTokens.length === 0) return facts.filter((fact) => hasAny(`${fact.key} ${factValue(fact)}`, meaningfulTokens));
|
|
4575
|
+
const keyFacts = facts.filter((fact) => queryKeyTokens.some((token) => tokenSet(fact.key).has(token)));
|
|
4576
|
+
const directMatches = keyFacts.filter((fact) => valueQueryTokens.length === 0 || valueQueryTokens.some((token) => tokenSet(factValue(fact)).has(token)));
|
|
4577
|
+
return directMatches;
|
|
4578
|
+
};
|
|
4579
|
+
let currentFacts = factMatchesFor(view.currentFacts);
|
|
4580
|
+
let historicalFacts = factMatchesFor(view.historicalFacts);
|
|
4581
|
+
const edgeMatches = (edge, supportingFacts) => {
|
|
4582
|
+
if (queryKeyTokens.length === 0) return hasAny(endpoint(node.id, edge), meaningfulTokens);
|
|
4583
|
+
return supportingFacts.length > 0 && hasAny(endpoint(node.id, edge), valueQueryTokens.length > 0 ? valueQueryTokens : queryTokens);
|
|
4584
|
+
};
|
|
4585
|
+
let currentEdges = view.currentEdges.filter((edge) => edgeMatches(edge, currentFacts));
|
|
4586
|
+
let historicalEdges = view.historicalEdges.filter((edge) => edgeMatches(edge, historicalFacts));
|
|
4587
|
+
if (queryKeyTokens.length === 0) {
|
|
4588
|
+
const overlap = (value) => {
|
|
4589
|
+
const available = tokenSet(value);
|
|
4590
|
+
return meaningfulTokens.filter((token) => available.has(token)).length;
|
|
4591
|
+
};
|
|
4592
|
+
const scoredFacts = [...currentFacts, ...historicalFacts].map((fact) => [fact.id, overlap(`${fact.key} ${factValue(fact)}`)]);
|
|
4593
|
+
const scoredEdges = [...currentEdges, ...historicalEdges].map((edge) => [edge.id, overlap(endpoint(node.id, edge))]);
|
|
4594
|
+
const best = Math.max(0, ...scoredFacts.map(([, score]) => score), ...scoredEdges.map(([, score]) => score));
|
|
4595
|
+
const bestFacts = new Set(scoredFacts.filter(([, score]) => score === best).map(([id]) => id));
|
|
4596
|
+
const bestEdges = new Set(scoredEdges.filter(([, score]) => score === best).map(([id]) => id));
|
|
4597
|
+
currentFacts = currentFacts.filter(({ id }) => bestFacts.has(id));
|
|
4598
|
+
historicalFacts = historicalFacts.filter(({ id }) => bestFacts.has(id));
|
|
4599
|
+
currentEdges = currentEdges.filter(({ id }) => bestEdges.has(id));
|
|
4600
|
+
historicalEdges = historicalEdges.filter(({ id }) => bestEdges.has(id));
|
|
4601
|
+
}
|
|
4602
|
+
const metadataFields = [
|
|
4603
|
+
["name", node.name],
|
|
4604
|
+
["aliases", node.aliases.join(" ")],
|
|
4605
|
+
["tags", (node.tags ?? []).join(" ")],
|
|
4606
|
+
["type", node.type]
|
|
4607
|
+
];
|
|
4608
|
+
const metadataMatches = metadataFields.filter(([, value]) => hasAny(value, queryTokens)).map(([field]) => field);
|
|
4609
|
+
const matchedMetadataEventIds = node.metadataProvenance ? dedupe([
|
|
4610
|
+
...metadataMatches.includes("name") ? node.metadataProvenance.name ?? [] : [],
|
|
4611
|
+
...(node.metadataProvenance.aliases ?? []).filter(({ value }) => hasAny(value, queryTokens)).flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4612
|
+
...(node.metadataProvenance.tags ?? []).filter(({ value }) => hasAny(value, queryTokens)).flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4613
|
+
...metadataMatches.includes("type") ? [...view.currentNodeEventIds, ...view.historicalNodeEventIds] : []
|
|
4614
|
+
]) : metadataMatches.length > 0 ? dedupe([...view.currentNodeEventIds, ...view.historicalNodeEventIds]) : [];
|
|
4615
|
+
const eventStatus = new Map(this.events.map((event) => [event.id, event.status]));
|
|
4616
|
+
const metadataCurrentHit = matchedMetadataEventIds.some((id) => eventStatus.get(id) === "active");
|
|
4617
|
+
const metadataHistoricalHit = matchedMetadataEventIds.some((id) => eventStatus.get(id) === "superseded");
|
|
4618
|
+
const currentRecordHit = currentFacts.length > 0 || currentEdges.length > 0;
|
|
4619
|
+
const historicalRecordHit = historicalFacts.length > 0 || historicalEdges.length > 0;
|
|
4620
|
+
const hasRecordHit = currentRecordHit || historicalRecordHit;
|
|
4621
|
+
const currentHit = currentRecordHit || !hasRecordHit && metadataMatches.length > 0 && metadataCurrentHit;
|
|
4622
|
+
const historicalHit = historicalRecordHit || !hasRecordHit && metadataMatches.length > 0 && metadataHistoricalHit;
|
|
4623
|
+
const relationMatches = [...view.currentEdges, ...view.historicalEdges].some((edge) => hasAny(edge.relation, queryTokens));
|
|
4624
|
+
const matchedFields = [
|
|
4625
|
+
...metadataMatches,
|
|
4626
|
+
...currentFacts.length > 0 ? ["currentFacts"] : [],
|
|
4627
|
+
...historicalFacts.length > 0 ? ["historicalFacts"] : [],
|
|
4628
|
+
...currentEdges.length > 0 ? ["currentEdgeEndpoints"] : [],
|
|
4629
|
+
...historicalEdges.length > 0 ? ["historicalEdgeEndpoints"] : [],
|
|
4630
|
+
...relationMatches ? ["relations"] : []
|
|
4631
|
+
];
|
|
4632
|
+
return {
|
|
4633
|
+
view,
|
|
4634
|
+
currentFacts,
|
|
4635
|
+
historicalFacts,
|
|
4636
|
+
currentEdges,
|
|
4637
|
+
historicalEdges,
|
|
4638
|
+
metadataMatches,
|
|
4639
|
+
matchedMetadataEventIds,
|
|
4640
|
+
currentHit,
|
|
4641
|
+
historicalHit,
|
|
4642
|
+
matchedFields
|
|
4643
|
+
};
|
|
4644
|
+
};
|
|
4645
|
+
const candidates = views.map(({ node }) => node);
|
|
4646
|
+
const ranked = bm25Rank(candidates, query, (node) => {
|
|
4647
|
+
const view = viewById.get(node.id);
|
|
4648
|
+
return weightedSearchTokens([
|
|
4649
|
+
[node.name, 6],
|
|
4650
|
+
[node.aliases.join(" "), 5],
|
|
4651
|
+
[(node.tags ?? []).join(" "), 5],
|
|
4652
|
+
[node.type, 2],
|
|
4653
|
+
[view.currentFacts.map((fact) => `${fact.key} ${factValue(fact)}`).join(" "), 4],
|
|
4654
|
+
[view.historicalFacts.map((fact) => `${fact.key} ${factValue(fact)}`).join(" "), 2],
|
|
4655
|
+
[view.currentEdges.map((edge) => endpoint(node.id, edge)).join(" "), 4],
|
|
4656
|
+
[view.historicalEdges.map((edge) => endpoint(node.id, edge)).join(" "), 2],
|
|
4657
|
+
[[...view.currentEdges, ...view.historicalEdges].map(({ relation }) => relation).join(" "), 1]
|
|
4658
|
+
]);
|
|
4659
|
+
}).filter(({ item }) => {
|
|
4660
|
+
const details = matchDetails(item);
|
|
4661
|
+
return details.currentHit || details.historicalHit;
|
|
4343
4662
|
}).slice(0, Math.max(1, Math.min(20, limit)));
|
|
4344
|
-
if (searchTokens(query).length > 0 && ranked.length === 0) return [];
|
|
4345
4663
|
return ranked.map(({ item: node, score }) => {
|
|
4346
|
-
const
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4664
|
+
const details = matchDetails(node);
|
|
4665
|
+
const matchedEventIds = dedupe([
|
|
4666
|
+
...details.currentFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4667
|
+
...details.historicalFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4668
|
+
...details.currentEdges.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4669
|
+
...details.historicalEdges.flatMap(({ sourceEventIds }) => sourceEventIds)
|
|
4670
|
+
]);
|
|
4671
|
+
const historicalKeys = new Set(details.historicalFacts.map(({ key }) => key));
|
|
4672
|
+
const historicalRelations = new Set(details.historicalEdges.map(({ relation }) => relation));
|
|
4673
|
+
const currentContextEventIds = dedupe([
|
|
4674
|
+
...details.view.currentFacts.filter(({ key }) => historicalKeys.has(key)).flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
4675
|
+
...details.view.currentEdges.filter(({ relation }) => historicalRelations.has(relation)).flatMap(({ sourceEventIds }) => sourceEventIds)
|
|
4676
|
+
]);
|
|
4677
|
+
const currentContextFacts = details.view.currentFacts.filter(({ key }) => historicalKeys.has(key));
|
|
4678
|
+
const currentContextEdges = details.view.currentEdges.filter(({ relation }) => historicalRelations.has(relation));
|
|
4679
|
+
const nameEventIds = node.metadataProvenance?.name ?? [];
|
|
4680
|
+
const metadataCandidates = dedupe([...details.matchedMetadataEventIds, ...nameEventIds]);
|
|
4681
|
+
const metadataOnly = matchedEventIds.length === 0 && details.metadataMatches.length > 0;
|
|
4682
|
+
const hasVisibleLegacyMetadata = !node.metadataProvenance && Boolean(node.name || node.aliases.length > 0 || (node.tags?.length ?? 0) > 0);
|
|
4683
|
+
const legacyMetadataEventIds = hasVisibleLegacyMetadata ? dedupe([...details.view.currentNodeEventIds, ...details.view.historicalNodeEventIds]) : [];
|
|
4684
|
+
const recordPriority = dedupe([
|
|
4685
|
+
...matchedEventIds,
|
|
4686
|
+
...currentContextEventIds
|
|
4687
|
+
]);
|
|
4688
|
+
const legacyMetadataFits = node.metadataProvenance !== void 0 || !hasVisibleLegacyMetadata || dedupe([...recordPriority, ...legacyMetadataEventIds]).length <= GRAPH_PROVENANCE_LIMIT;
|
|
4689
|
+
const legacyMetadataNotExpanded = hasVisibleLegacyMetadata && !legacyMetadataFits;
|
|
4690
|
+
const prioritized = node.metadataProvenance ? dedupe([
|
|
4691
|
+
...matchedEventIds,
|
|
4692
|
+
...metadataOnly ? metadataCandidates : [],
|
|
4693
|
+
...currentContextEventIds,
|
|
4694
|
+
...!metadataOnly ? metadataCandidates : []
|
|
4695
|
+
]) : recordPriority;
|
|
4696
|
+
const provenanceEventIds = dedupe([
|
|
4697
|
+
...prioritized,
|
|
4698
|
+
...legacyMetadataFits ? legacyMetadataEventIds : []
|
|
4699
|
+
]).slice(0, GRAPH_PROVENANCE_LIMIT);
|
|
4700
|
+
const evidenceIds = new Set(provenanceEventIds);
|
|
4701
|
+
const boundedView = boundEffectiveGraphNodeView(details.view, evidenceIds, {
|
|
4702
|
+
preserveLegacyMetadata: legacyMetadataNotExpanded
|
|
4703
|
+
});
|
|
4704
|
+
const bounded = (record2) => {
|
|
4705
|
+
const sourceEventIds = record2.sourceEventIds.filter((id) => evidenceIds.has(id));
|
|
4706
|
+
return sourceEventIds.length > 0 ? { ...record2, sourceEventIds } : null;
|
|
4707
|
+
};
|
|
4708
|
+
const matchType = details.currentHit && details.historicalHit ? "both" : details.historicalHit ? "historical" : "current";
|
|
4350
4709
|
return {
|
|
4351
|
-
node,
|
|
4710
|
+
node: boundedView.node,
|
|
4352
4711
|
score,
|
|
4353
|
-
matchedFields,
|
|
4354
|
-
matchReason: `
|
|
4712
|
+
matchedFields: details.matchedFields,
|
|
4713
|
+
matchReason: `Meaningful ${matchType} match in ${details.matchedFields.join(", ")}; relation text is ranking-only.`,
|
|
4714
|
+
matchType,
|
|
4715
|
+
currentFacts: dedupe([...details.currentFacts, ...currentContextFacts]).flatMap((record2) => bounded(record2) ?? []),
|
|
4716
|
+
historicalFacts: details.historicalFacts.flatMap((record2) => bounded(record2) ?? []),
|
|
4717
|
+
currentEdges: dedupe([...details.currentEdges, ...currentContextEdges]).flatMap((record2) => bounded(record2) ?? []),
|
|
4718
|
+
historicalEdges: details.historicalEdges.flatMap((record2) => bounded(record2) ?? []),
|
|
4719
|
+
provenanceEventIds,
|
|
4720
|
+
...legacyMetadataNotExpanded ? { metadataEvidenceStatus: "not_expanded" } : {},
|
|
4721
|
+
timeline: graphTimeline(provenanceEventIds, this.events)
|
|
4355
4722
|
};
|
|
4356
4723
|
});
|
|
4357
4724
|
}
|
|
@@ -5148,6 +5515,20 @@ var GRAPH_FACT = {
|
|
|
5148
5515
|
additionalProperties: false,
|
|
5149
5516
|
properties: { key: { type: "string", required: true }, value: { ...VALUE, required: true }, sourceEventIds: { ...STRING_ARRAY, required: true } }
|
|
5150
5517
|
};
|
|
5518
|
+
var GRAPH_METADATA_ENTRY = {
|
|
5519
|
+
type: "object",
|
|
5520
|
+
additionalProperties: false,
|
|
5521
|
+
properties: { value: { type: "string", required: true }, sourceEventIds: { ...STRING_ARRAY, required: true } }
|
|
5522
|
+
};
|
|
5523
|
+
var GRAPH_METADATA_PROVENANCE = {
|
|
5524
|
+
type: "object",
|
|
5525
|
+
additionalProperties: false,
|
|
5526
|
+
properties: {
|
|
5527
|
+
name: { ...STRING_ARRAY, required: true },
|
|
5528
|
+
aliases: { type: "array", items: GRAPH_METADATA_ENTRY },
|
|
5529
|
+
tags: { type: "array", items: GRAPH_METADATA_ENTRY }
|
|
5530
|
+
}
|
|
5531
|
+
};
|
|
5151
5532
|
var GRAPH_NODE = {
|
|
5152
5533
|
type: "object",
|
|
5153
5534
|
additionalProperties: false,
|
|
@@ -5157,6 +5538,7 @@ var GRAPH_NODE = {
|
|
|
5157
5538
|
type: { type: "string", enum: ["person", "project", "organization", "tool", "place"], required: true },
|
|
5158
5539
|
aliases: STRING_ARRAY,
|
|
5159
5540
|
tags: { ...STRING_ARRAY, required: true },
|
|
5541
|
+
metadataProvenance: { ...GRAPH_METADATA_PROVENANCE, required: true },
|
|
5160
5542
|
state: { type: "string" },
|
|
5161
5543
|
facts: { type: "array", items: GRAPH_FACT },
|
|
5162
5544
|
status: { type: "string", enum: ["active", "superseded", "disputed", "archived"] },
|
|
@@ -5263,6 +5645,7 @@ function compactGraphProjectionContext(context) {
|
|
|
5263
5645
|
type: node.type,
|
|
5264
5646
|
aliases: node.aliases.slice(0, 12),
|
|
5265
5647
|
tags: node.tags?.slice(0, 12),
|
|
5648
|
+
metadataProvenance: node.metadataProvenance,
|
|
5266
5649
|
currentState: node.currentState.slice(0, 600),
|
|
5267
5650
|
facts: node.facts.filter(({ status }) => status === "active" || status === "disputed").slice(-12).map((fact) => ({
|
|
5268
5651
|
key: fact.key,
|
|
@@ -5395,7 +5778,7 @@ var DshModelBridge = class {
|
|
|
5395
5778
|
const eventIds = new Set(context.events.map((event) => event.id));
|
|
5396
5779
|
const raw = object(await this.callStructured(
|
|
5397
5780
|
"graphProjector",
|
|
5398
|
-
`Project the supplied Events into the current Knowledge Graph, then call ${STRUCTURED_TOOLS.graphProjector.name} exactly once. Events are the sole source of truth; never use legacy Element data. Return only nodes and edges touched by the supplied Events; never echo unchanged historical graph records. Return at most 24 nodes and 32 edges. Use stable entity nodes for people, projects, organizations, tools, and places. Use aliases to merge spelling/case/separator variants. Give every returned node 1-6 concise semantic role tags such as benchmark, evaluation, memory-plugin, parser, or development-tool; tags describe the node's specific role and never replace its person/project/organization/tool/place type. Reuse stable tag wording when possible. Put attributes in node facts and every relationship in a directed edge using fromRef/toRef\u2014never encode a relationship as a fact string. Prefer concise canonical Chinese relation labels such as \u4F7F\u7528\u3001\u5C5E\u4E8E\u3001\u521B\u5EFA\u3001\u53C2\u4E0E\u3001\u8D21\u732E\u3001\u4F9D\u8D56\u3001\u4F4D\u4E8E\u3001\u76F8\u5173. Every node, fact, and
|
|
5781
|
+
`Project the supplied Events into the current Knowledge Graph, then call ${STRUCTURED_TOOLS.graphProjector.name} exactly once. Events are the sole source of truth; never use legacy Element data. Return only nodes and edges touched by the supplied Events; never echo unchanged historical graph records. Return at most 24 nodes and 32 edges. Use stable entity nodes for people, projects, organizations, tools, and places. Use aliases to merge spelling/case/separator variants. Give every returned node 1-6 concise semantic role tags such as benchmark, evaluation, memory-plugin, parser, or development-tool; tags describe the node's specific role and never replace its person/project/organization/tool/place type. Reuse stable tag wording when possible. For every node name, alias, and tag, include metadataProvenance with the exact supplied Event ids that support that individual value; never use an unrelated active Event as a substitute. Put attributes in node facts and every relationship in a directed edge using fromRef/toRef\u2014never encode a relationship as a fact string. Prefer concise canonical Chinese relation labels such as \u4F7F\u7528\u3001\u5C5E\u4E8E\u3001\u521B\u5EFA\u3001\u53C2\u4E0E\u3001\u8D21\u732E\u3001\u4F9D\u8D56\u3001\u4F4D\u4E8E\u3001\u76F8\u5173. Every node, fact, edge, and metadata provenance id must cite only supplied Event ids. Do not return text.`,
|
|
5399
5782
|
compactGraphProjectionContext(context)
|
|
5400
5783
|
));
|
|
5401
5784
|
const nodes = (Array.isArray(raw.nodes) ? raw.nodes : []).flatMap((candidate) => {
|
|
@@ -5411,12 +5794,31 @@ var DshModelBridge = class {
|
|
|
5411
5794
|
if (sourceEventIds2.length === 0) return [];
|
|
5412
5795
|
return [{ key: text2(fact.key), value, sourceEventIds: sourceEventIds2 }];
|
|
5413
5796
|
});
|
|
5797
|
+
const rawMetadata = object(item.metadataProvenance);
|
|
5798
|
+
const metadataEntries2 = (value) => (Array.isArray(value) ? value : []).flatMap((candidate2) => {
|
|
5799
|
+
const entry = object(candidate2);
|
|
5800
|
+
const value2 = text2(entry.value);
|
|
5801
|
+
const sourceEventIds2 = strings2(entry.sourceEventIds).filter((id) => eventIds.has(id));
|
|
5802
|
+
return value2 && sourceEventIds2.length > 0 ? [{ value: value2, sourceEventIds: sourceEventIds2 }] : [];
|
|
5803
|
+
});
|
|
5804
|
+
const metadataName = strings2(rawMetadata.name).filter((id) => eventIds.has(id));
|
|
5805
|
+
if (metadataName.length === 0) {
|
|
5806
|
+
throw new Error(`Graph projection validation failed: node "${text2(item.ref)}" name "${text2(item.name)}" lacks valid metadata provenance.`);
|
|
5807
|
+
}
|
|
5808
|
+
const metadataAliases = metadataEntries2(rawMetadata.aliases);
|
|
5809
|
+
const metadataTags = metadataEntries2(rawMetadata.tags);
|
|
5810
|
+
const metadataProvenance = {
|
|
5811
|
+
...metadataName.length > 0 ? { name: metadataName } : {},
|
|
5812
|
+
...metadataAliases.length > 0 ? { aliases: metadataAliases } : {},
|
|
5813
|
+
...metadataTags.length > 0 ? { tags: metadataTags } : {}
|
|
5814
|
+
};
|
|
5414
5815
|
return [{
|
|
5415
5816
|
ref: text2(item.ref),
|
|
5416
5817
|
name: text2(item.name),
|
|
5417
5818
|
type,
|
|
5418
5819
|
aliases: strings2(item.aliases),
|
|
5419
5820
|
tags: strings2(item.tags).slice(0, 12),
|
|
5821
|
+
...Object.keys(metadataProvenance).length > 0 ? { metadataProvenance } : {},
|
|
5420
5822
|
...text2(item.state) ? { state: text2(item.state) } : {},
|
|
5421
5823
|
facts,
|
|
5422
5824
|
...typeof item.status === "string" ? { status: item.status } : {},
|
|
@@ -5605,8 +6007,9 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
5605
6007
|
}
|
|
5606
6008
|
}
|
|
5607
6009
|
}
|
|
6010
|
+
const validationDetail = lastError?.message ? `: ${lastError.message}` : "";
|
|
5608
6011
|
throw new ModelJsonResponseError(
|
|
5609
|
-
`StrataGate model did not produce a valid ${STRUCTURED_TOOLS[kind].name} call after ${attemptsUsed} attempt${attemptsUsed === 1 ? "" : "s"}`,
|
|
6012
|
+
`StrataGate model did not produce a valid ${STRUCTURED_TOOLS[kind].name} call after ${attemptsUsed} attempt${attemptsUsed === 1 ? "" : "s"}${validationDetail}`,
|
|
5610
6013
|
{ cause: lastError, response: lastResponse }
|
|
5611
6014
|
);
|
|
5612
6015
|
}
|
|
@@ -6618,31 +7021,36 @@ var StrataGateRuntime = class {
|
|
|
6618
7021
|
const openTail = memory.listOpenTail(threadId);
|
|
6619
7022
|
const activationQuery = [currentUserMessage(session), renderMessages(recentTurns(openTail, 2))].filter(Boolean).join("\n\n");
|
|
6620
7023
|
const eventHits = activationQuery ? await memory.searchEvents(activationQuery, { limit: 20 }) : [];
|
|
6621
|
-
let
|
|
7024
|
+
let graphResults = [];
|
|
6622
7025
|
if (activationQuery && typeof memory.searchGraphNodes === "function") {
|
|
6623
|
-
|
|
7026
|
+
graphResults = await memory.searchGraphNodes(activationQuery, 12);
|
|
6624
7027
|
} else if (activationQuery) {
|
|
6625
7028
|
const legacy = activatedElements(memory, await memory.searchElements(activationQuery, { limit: 12 }));
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
7029
|
+
graphResults = legacy.map((item) => ({
|
|
7030
|
+
node: {
|
|
7031
|
+
id: item.elementId,
|
|
7032
|
+
name: item.name,
|
|
7033
|
+
type: item.type,
|
|
7034
|
+
aliases: [],
|
|
7035
|
+
currentState: "",
|
|
7036
|
+
status: "active",
|
|
7037
|
+
confidence: item.fact.confidence ?? 0.8,
|
|
7038
|
+
sourceEventIds: item.fact.sourceEventIds,
|
|
7039
|
+
facts: [{ ...item.fact, confidence: item.fact.confidence ?? 0.8, status: item.fact.status === "disputed" ? "disputed" : item.fact.status === "superseded" ? "superseded" : "active" }],
|
|
7040
|
+
createdAt: item.fact.createdAt,
|
|
7041
|
+
updatedAt: item.fact.updatedAt
|
|
7042
|
+
},
|
|
7043
|
+
score: item.score,
|
|
7044
|
+
matchType: item.fact.status === "superseded" ? "historical" : "current",
|
|
7045
|
+
provenanceEventIds: item.fact.sourceEventIds
|
|
6638
7046
|
}));
|
|
6639
7047
|
}
|
|
6640
7048
|
const events = activatedEvents(memory, eventHits);
|
|
6641
7049
|
const currentBlockIds = new Set(memory.listBlocks().filter((block) => block.threadId === threadId).map(({ id }) => id));
|
|
6642
7050
|
const currentEventIds = new Set(memory.listEvents().filter((event) => currentBlockIds.has(event.sourceBlockId)).map(({ id }) => id));
|
|
6643
7051
|
const longTermEvents = events.filter((event) => !currentEventIds.has(event.id)).slice(0, AUTO_EVENT_LIMIT);
|
|
6644
|
-
|
|
6645
|
-
return renderActivatedMemory(longTermEvents,
|
|
7052
|
+
graphResults = graphResults.filter((result) => !(result.provenanceEventIds ?? result.node.sourceEventIds).some((id) => currentEventIds.has(id))).slice(0, AUTO_ELEMENT_LIMIT);
|
|
7053
|
+
return renderActivatedMemory(longTermEvents, graphResults);
|
|
6646
7054
|
}
|
|
6647
7055
|
/**
|
|
6648
7056
|
* Replace the just-sealed DSH turns with one native surface message. The raw
|
|
@@ -7428,29 +7836,56 @@ var StrataGateRuntime = class {
|
|
|
7428
7836
|
async searchGraph(session, query, limit = 8) {
|
|
7429
7837
|
await this.flush();
|
|
7430
7838
|
const results = await (await this.space(session)).searchGraphNodes(query, limit);
|
|
7431
|
-
return this.batch(session, results.map(({ node }) => ({
|
|
7839
|
+
return this.batch(session, results.map(({ node, provenanceEventIds }) => ({
|
|
7432
7840
|
ref: `graph-node:${node.id}`,
|
|
7433
7841
|
target: {
|
|
7434
|
-
eventIds:
|
|
7842
|
+
eventIds: (provenanceEventIds ?? []).slice(0, GRAPH_PROVENANCE_LIMIT),
|
|
7435
7843
|
elementIds: [],
|
|
7436
7844
|
citation: citation("graph", node.id, node.name, `graph-node:${node.id}`, "nodeId")
|
|
7437
7845
|
}
|
|
7438
|
-
})), results.map(
|
|
7846
|
+
})), results.map(compactGraphNode));
|
|
7439
7847
|
}
|
|
7440
7848
|
async expandGraphNode(session, id) {
|
|
7441
7849
|
await this.flush();
|
|
7442
7850
|
const memory = await this.space(session);
|
|
7443
7851
|
const node = memory.listGraphNodes().find((candidate) => candidate.id === id);
|
|
7444
7852
|
if (!node) throw new Error(`Unknown graph node: ${id}`);
|
|
7445
|
-
const
|
|
7853
|
+
const view = effectiveGraphNodeView(node, memory.listGraphEdges(), memory.listEvents());
|
|
7854
|
+
if (!view) throw new Error(`Graph node ${id} has no retrievable Event evidence`);
|
|
7855
|
+
const provenanceEventIds = [.../* @__PURE__ */ new Set([
|
|
7856
|
+
...view.currentFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
7857
|
+
...view.currentEdges.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
7858
|
+
...view.historicalFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
7859
|
+
...view.historicalEdges.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
7860
|
+
...view.currentNodeEventIds,
|
|
7861
|
+
...view.historicalNodeEventIds
|
|
7862
|
+
])].slice(0, GRAPH_PROVENANCE_LIMIT);
|
|
7863
|
+
const legacyMetadataOverflow = !view.node.metadataProvenance && view.node.sourceEventIds.length > GRAPH_PROVENANCE_LIMIT;
|
|
7864
|
+
const boundedView = boundEffectiveGraphNodeView(view, new Set(provenanceEventIds), {
|
|
7865
|
+
preserveLegacyMetadata: legacyMetadataOverflow
|
|
7866
|
+
});
|
|
7867
|
+
const currentFacts = boundedView.currentFacts;
|
|
7868
|
+
const historicalFacts = boundedView.historicalFacts;
|
|
7869
|
+
const currentEdges = boundedView.currentEdges;
|
|
7870
|
+
const historicalEdges = boundedView.historicalEdges;
|
|
7446
7871
|
return this.batch(session, [{
|
|
7447
7872
|
ref: `graph-node:${node.id}:expanded`,
|
|
7448
7873
|
target: {
|
|
7449
|
-
eventIds:
|
|
7874
|
+
eventIds: provenanceEventIds,
|
|
7450
7875
|
elementIds: [],
|
|
7451
|
-
citation: citation("graph", node.id, node.name, `graph-node:${node.id}:expanded`, "nodeId", { expanded: true })
|
|
7876
|
+
citation: citation("graph", node.id, boundedView.node.name, `graph-node:${node.id}:expanded`, "nodeId", { expanded: true })
|
|
7452
7877
|
}
|
|
7453
|
-
}], {
|
|
7878
|
+
}], {
|
|
7879
|
+
node: boundedView.node,
|
|
7880
|
+
edges: [...currentEdges, ...historicalEdges],
|
|
7881
|
+
currentFacts,
|
|
7882
|
+
historicalFacts,
|
|
7883
|
+
currentEdges,
|
|
7884
|
+
historicalEdges,
|
|
7885
|
+
provenanceEventIds,
|
|
7886
|
+
...legacyMetadataOverflow ? { metadataEvidenceStatus: "not_expanded" } : {},
|
|
7887
|
+
timeline: graphTimeline(provenanceEventIds, memory.listEvents())
|
|
7888
|
+
});
|
|
7454
7889
|
}
|
|
7455
7890
|
scheduleBlockDerivation(session, memory) {
|
|
7456
7891
|
const threadId = String(session.id);
|
|
@@ -7762,18 +8197,27 @@ function compactEvent(event, score) {
|
|
|
7762
8197
|
scoreMeaning: "Ranking-only BM25/RRF score; not confidence, probability, or factual accuracy."
|
|
7763
8198
|
};
|
|
7764
8199
|
}
|
|
7765
|
-
function compactGraphNode(
|
|
8200
|
+
function compactGraphNode(result) {
|
|
8201
|
+
const { node, score, matchedFields, matchReason } = result;
|
|
7766
8202
|
return {
|
|
7767
8203
|
id: node.id,
|
|
7768
8204
|
name: node.name,
|
|
7769
8205
|
type: node.type,
|
|
7770
8206
|
aliases: node.aliases.map((alias) => compactText2(alias, 160)),
|
|
8207
|
+
...node.tags ? { tags: node.tags.map((tag) => compactText2(tag, 120)) } : {},
|
|
7771
8208
|
currentState: compactText2(node.currentState, 500),
|
|
7772
8209
|
status: node.status,
|
|
7773
8210
|
rankScore: score,
|
|
7774
8211
|
...matchedFields ? { matchedFields } : {},
|
|
7775
8212
|
...matchReason ? { matchReason } : {},
|
|
7776
|
-
|
|
8213
|
+
...result.metadataEvidenceStatus ? { metadataEvidenceStatus: result.metadataEvidenceStatus } : {},
|
|
8214
|
+
scoreMeaning: "Ranking-only BM25/RRF score; not confidence, probability, or factual accuracy.",
|
|
8215
|
+
...result.matchType ? { matchType: result.matchType } : {},
|
|
8216
|
+
...result.currentFacts ? { currentMatches: result.currentFacts } : {},
|
|
8217
|
+
...result.historicalFacts ? { historicalMatches: result.historicalFacts } : {},
|
|
8218
|
+
...result.currentEdges ? { currentRelations: result.currentEdges } : {},
|
|
8219
|
+
...result.historicalEdges ? { historicalRelations: result.historicalEdges } : {},
|
|
8220
|
+
...result.timeline ? { timeline: result.timeline } : {}
|
|
7777
8221
|
};
|
|
7778
8222
|
}
|
|
7779
8223
|
function compactRawHit(result) {
|
|
@@ -7848,7 +8292,7 @@ function activatedElements(memory, relevance) {
|
|
|
7848
8292
|
weight
|
|
7849
8293
|
]).map(({ item }) => item);
|
|
7850
8294
|
}
|
|
7851
|
-
function renderActivatedMemory(events,
|
|
8295
|
+
function renderActivatedMemory(events, graphResults) {
|
|
7852
8296
|
const heading = [
|
|
7853
8297
|
"[Activated long-term memory]",
|
|
7854
8298
|
"Historical memory context.",
|
|
@@ -7877,13 +8321,23 @@ Events:
|
|
|
7877
8321
|
tokens += cost;
|
|
7878
8322
|
eventCount += 1;
|
|
7879
8323
|
}
|
|
7880
|
-
for (const
|
|
8324
|
+
for (const result of graphResults) {
|
|
8325
|
+
const node = result.node;
|
|
7881
8326
|
const rendered = JSON.stringify({
|
|
7882
8327
|
nodeId: node.id,
|
|
7883
8328
|
name: node.name,
|
|
7884
8329
|
type: node.type,
|
|
8330
|
+
aliases: node.aliases,
|
|
8331
|
+
tags: node.tags,
|
|
8332
|
+
status: node.status,
|
|
8333
|
+
metadataEvidenceStatus: result.metadataEvidenceStatus,
|
|
8334
|
+
matchType: result.matchType,
|
|
7885
8335
|
currentState: node.currentState,
|
|
7886
|
-
facts: node.facts.
|
|
8336
|
+
facts: node.facts.map(({ key, value, status, validFrom, validTo }) => ({ key, value, status, validFrom, validTo })),
|
|
8337
|
+
currentRelations: result.currentEdges?.map(({ relation, status }) => ({ relation, status })),
|
|
8338
|
+
historicalMatches: result.historicalFacts?.map(({ key, value, status, validFrom, validTo }) => ({ key, value, status, validFrom, validTo })),
|
|
8339
|
+
historicalRelations: result.historicalEdges?.map(({ relation, status, validFrom, validTo }) => ({ relation, status, validFrom, validTo })),
|
|
8340
|
+
timeline: result.timeline
|
|
7887
8341
|
});
|
|
7888
8342
|
const cost = estimateTokens(`
|
|
7889
8343
|
KnowledgeGraph:
|
|
@@ -7961,7 +8415,7 @@ function registerMemoryTools(ctx, runtime) {
|
|
|
7961
8415
|
}));
|
|
7962
8416
|
ctx.tools.register(defineTool({
|
|
7963
8417
|
name: "memory_search_graph",
|
|
7964
|
-
description: "Search the
|
|
8418
|
+
description: "Search the Event-backed Knowledge Graph for current state and query-relevant history. Results explicitly label current, historical, or both; historical matches never imply current truth, disputed records remain marked, and Event evidence is bounded to the match. Endpoint names and aliases can match relations, while relation text alone is ranking context. rankScore is ranking-only, never confidence or factual accuracy.",
|
|
7965
8419
|
parameters: {
|
|
7966
8420
|
query: { type: "string", required: true },
|
|
7967
8421
|
limit: { type: "integer", description: "Maximum results, 1-20." }
|
|
@@ -7971,7 +8425,7 @@ function registerMemoryTools(ctx, runtime) {
|
|
|
7971
8425
|
}));
|
|
7972
8426
|
ctx.tools.register(defineTool({
|
|
7973
8427
|
name: "memory_expand_graph_node",
|
|
7974
|
-
description: "Expand one Knowledge Graph node
|
|
8428
|
+
description: "Expand one Knowledge Graph node through the same Event-authoritative view as search: dynamic current state, marked disputed records, retrievable history, and bounded supporting Event evidence. Forgotten or archived Event provenance is never exposed.",
|
|
7975
8429
|
parameters: { id: { type: "string", required: true } },
|
|
7976
8430
|
output: jsonOutput,
|
|
7977
8431
|
execute: async (args, exec) => runtime.expandGraphNode(sessionOf(exec), args.id)
|
|
@@ -9095,17 +9549,36 @@ async function sources(runtime, url) {
|
|
|
9095
9549
|
events = [event];
|
|
9096
9550
|
ids = new Set(event.sourceMessageIds);
|
|
9097
9551
|
} else if (nodeId) {
|
|
9098
|
-
const
|
|
9099
|
-
if (!
|
|
9100
|
-
|
|
9552
|
+
const rawNode = snapshot.graphNodes.find(({ id }) => id === nodeId);
|
|
9553
|
+
if (!rawNode) throw new AdminHttpError(404, `Unknown graph node: ${nodeId}`);
|
|
9554
|
+
const view = effectiveGraphNodeView(rawNode, snapshot.graphEdges, snapshot.events);
|
|
9555
|
+
if (!view) throw new AdminHttpError(404, `Graph node has no retrievable Event evidence: ${nodeId}`);
|
|
9556
|
+
const edges = [...view.currentEdges, ...view.historicalEdges];
|
|
9557
|
+
const relatedNodeIds = /* @__PURE__ */ new Set([rawNode.id, ...edges.flatMap(({ fromNodeId, toNodeId }) => [fromNodeId, toNodeId])]);
|
|
9558
|
+
const nodes = snapshot.graphNodes.filter(({ id }) => relatedNodeIds.has(id)).flatMap((candidate) => effectiveGraphNodeView(candidate, snapshot.graphEdges, snapshot.events)?.node ?? []);
|
|
9559
|
+
const metadataEventIds = [
|
|
9560
|
+
...view.node.metadataProvenance?.name ?? [],
|
|
9561
|
+
...(view.node.metadataProvenance?.aliases ?? []).flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
9562
|
+
...(view.node.metadataProvenance?.tags ?? []).flatMap(({ sourceEventIds }) => sourceEventIds)
|
|
9563
|
+
];
|
|
9564
|
+
const eventIds = /* @__PURE__ */ new Set([
|
|
9565
|
+
...view.node.sourceEventIds,
|
|
9566
|
+
...metadataEventIds,
|
|
9567
|
+
...view.currentFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
9568
|
+
...view.historicalFacts.flatMap(({ sourceEventIds }) => sourceEventIds),
|
|
9569
|
+
...edges.flatMap(({ sourceEventIds }) => sourceEventIds)
|
|
9570
|
+
]);
|
|
9571
|
+
events = snapshot.events.filter(({ id }) => eventIds.has(id));
|
|
9101
9572
|
ids = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
|
|
9102
|
-
const edges = snapshot.graphEdges.filter(({ fromNodeId, toNodeId }) => fromNodeId === node.id || toNodeId === node.id);
|
|
9103
|
-
const relatedNodeIds = /* @__PURE__ */ new Set([node.id, ...edges.flatMap(({ fromNodeId, toNodeId }) => [fromNodeId, toNodeId])]);
|
|
9104
9573
|
return {
|
|
9105
9574
|
namespace,
|
|
9106
|
-
node,
|
|
9107
|
-
nodes
|
|
9575
|
+
node: view.node,
|
|
9576
|
+
nodes,
|
|
9108
9577
|
edges,
|
|
9578
|
+
currentFacts: view.currentFacts,
|
|
9579
|
+
historicalFacts: view.historicalFacts,
|
|
9580
|
+
currentEdges: view.currentEdges,
|
|
9581
|
+
historicalEdges: view.historicalEdges,
|
|
9109
9582
|
events: events.map((event) => eventSummary(event)),
|
|
9110
9583
|
messages: sourceMessages(snapshot, ids)
|
|
9111
9584
|
};
|
|
@@ -9139,7 +9612,7 @@ async function sources(runtime, url) {
|
|
|
9139
9612
|
namespace,
|
|
9140
9613
|
events: events.map((event) => eventSummary(event, eventId ? snapshot : void 0)),
|
|
9141
9614
|
...eventId ? {
|
|
9142
|
-
relatedNodes: snapshot.graphNodes.filter(({ id }) => events[0]?.temporal.participantNodeIds?.includes(id)).map(({ id, name: name2, type, aliases }) => ({ id, name: name2, type, aliases }))
|
|
9615
|
+
relatedNodes: snapshot.graphNodes.filter(({ id }) => events[0]?.temporal.participantNodeIds?.includes(id)).flatMap((candidate) => effectiveGraphNodeView(candidate, snapshot.graphEdges, snapshot.events)?.node ?? []).map(({ id, name: name2, type, aliases }) => ({ id, name: name2, type, aliases }))
|
|
9143
9616
|
} : {},
|
|
9144
9617
|
elements: elements.map(elementSummary),
|
|
9145
9618
|
blocks: events.map((event) => snapshot.blocks.find(({ id }) => id === event.sourceBlockId)).filter((block) => Boolean(block)).map((block) => ({ id: block.id, title: block.l0Title, createdAt: block.createdAt })),
|