stratagate-dsh 0.2.74 → 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 +12 -0
- package/dist/client.js +6 -4
- package/dist/plugin.js +621 -90
- 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
|
}
|
|
@@ -6042,11 +6445,68 @@ function workspaceDisplayName(cwd) {
|
|
|
6042
6445
|
function feedbackText(value, maximum) {
|
|
6043
6446
|
return typeof value === "string" ? value.trim().slice(0, maximum) : "";
|
|
6044
6447
|
}
|
|
6448
|
+
var FEEDBACK_MARKDOWN_HEADINGS = {
|
|
6449
|
+
description: "\u95EE\u9898\u63CF\u8FF0",
|
|
6450
|
+
reproduction: "\u590D\u73B0\u6B65\u9AA4",
|
|
6451
|
+
expected: "\u9884\u671F\u884C\u4E3A",
|
|
6452
|
+
actual: "\u5B9E\u9645\u884C\u4E3A",
|
|
6453
|
+
errorContext: "\u76F8\u5173\u9519\u8BEF\u4FE1\u606F"
|
|
6454
|
+
};
|
|
6455
|
+
function patchFeedbackMarkdown(markdown, input) {
|
|
6456
|
+
let result = markdown.trim();
|
|
6457
|
+
for (const [key, heading] of Object.entries(FEEDBACK_MARKDOWN_HEADINGS)) {
|
|
6458
|
+
if (!Object.prototype.hasOwnProperty.call(input, key)) continue;
|
|
6459
|
+
const raw = input[key];
|
|
6460
|
+
const text3 = Array.isArray(raw) ? raw.map((item, index) => {
|
|
6461
|
+
const value = feedbackText(item, 2e3);
|
|
6462
|
+
return value ? `${index + 1}. ${value}` : "";
|
|
6463
|
+
}).filter(Boolean).join("\n") : feedbackText(raw, key === "description" ? 2e4 : key === "expected" || key === "actual" ? 1e4 : 2e4);
|
|
6464
|
+
const marker = `## ${heading}`;
|
|
6465
|
+
const lines = result.split("\n");
|
|
6466
|
+
let fence = null;
|
|
6467
|
+
let sectionStart = -1;
|
|
6468
|
+
let sectionEnd = lines.length;
|
|
6469
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
6470
|
+
const line = (lines[index] ?? "").replace(/\r$/, "");
|
|
6471
|
+
if (fence) {
|
|
6472
|
+
const closingFence = line.match(/^ {0,3}(`+|~+)[ \t]*$/);
|
|
6473
|
+
if (closingFence?.[1]?.[0] === fence.character && closingFence[1].length >= fence.length) fence = null;
|
|
6474
|
+
continue;
|
|
6475
|
+
}
|
|
6476
|
+
const openingFence = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
6477
|
+
if (openingFence?.[1] && (openingFence[1][0] === "~" || !openingFence[2]?.includes("`"))) {
|
|
6478
|
+
fence = { character: openingFence[1][0], length: openingFence[1].length };
|
|
6479
|
+
continue;
|
|
6480
|
+
}
|
|
6481
|
+
if (line === marker && sectionStart < 0) {
|
|
6482
|
+
sectionStart = index;
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
if (sectionStart >= 0 && /^##\s+\S/.test(line)) {
|
|
6486
|
+
sectionEnd = index;
|
|
6487
|
+
break;
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
if (sectionStart < 0) {
|
|
6491
|
+
if (text3) result = `${result ? `${result}
|
|
6492
|
+
|
|
6493
|
+
` : ""}${marker}
|
|
6494
|
+
|
|
6495
|
+
${text3}`;
|
|
6496
|
+
continue;
|
|
6497
|
+
}
|
|
6498
|
+
const replacement = text3 ? [marker, "", ...text3.split("\n")] : [];
|
|
6499
|
+
lines.splice(sectionStart, sectionEnd - sectionStart, ...replacement);
|
|
6500
|
+
result = lines.join("\n").trim();
|
|
6501
|
+
}
|
|
6502
|
+
return result.trim();
|
|
6503
|
+
}
|
|
6045
6504
|
function normalizeFeedbackDraft(input, previous) {
|
|
6046
6505
|
const has = (key) => Object.prototype.hasOwnProperty.call(input, key);
|
|
6047
6506
|
const replacesStructuredBody = has("bodyMarkdown");
|
|
6507
|
+
const updatesStructuredBody = ["description", "reproduction", "expected", "actual", "errorContext"].some((key) => has(key));
|
|
6048
6508
|
const reproduction = replacesStructuredBody ? [] : has("reproduction") ? (Array.isArray(input.reproduction) ? input.reproduction : []).map((value) => feedbackText(value, 2e3)).filter(Boolean).slice(0, 20) : previous?.reproduction ?? [];
|
|
6049
|
-
const bodyMarkdown = has("bodyMarkdown") ? feedbackText(input.bodyMarkdown, 5e4) : previous?.bodyMarkdown;
|
|
6509
|
+
const bodyMarkdown = has("bodyMarkdown") ? feedbackText(input.bodyMarkdown, 5e4) : updatesStructuredBody && previous?.bodyMarkdown ? patchFeedbackMarkdown(previous.bodyMarkdown, input) : previous?.bodyMarkdown;
|
|
6050
6510
|
return {
|
|
6051
6511
|
title: has("title") ? feedbackText(input.title, 240) : previous?.title ?? "",
|
|
6052
6512
|
description: replacesStructuredBody ? "" : has("description") ? feedbackText(input.description, 2e4) : previous?.description ?? "",
|
|
@@ -6561,31 +7021,36 @@ var StrataGateRuntime = class {
|
|
|
6561
7021
|
const openTail = memory.listOpenTail(threadId);
|
|
6562
7022
|
const activationQuery = [currentUserMessage(session), renderMessages(recentTurns(openTail, 2))].filter(Boolean).join("\n\n");
|
|
6563
7023
|
const eventHits = activationQuery ? await memory.searchEvents(activationQuery, { limit: 20 }) : [];
|
|
6564
|
-
let
|
|
7024
|
+
let graphResults = [];
|
|
6565
7025
|
if (activationQuery && typeof memory.searchGraphNodes === "function") {
|
|
6566
|
-
|
|
7026
|
+
graphResults = await memory.searchGraphNodes(activationQuery, 12);
|
|
6567
7027
|
} else if (activationQuery) {
|
|
6568
7028
|
const legacy = activatedElements(memory, await memory.searchElements(activationQuery, { limit: 12 }));
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
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
|
|
6581
7046
|
}));
|
|
6582
7047
|
}
|
|
6583
7048
|
const events = activatedEvents(memory, eventHits);
|
|
6584
7049
|
const currentBlockIds = new Set(memory.listBlocks().filter((block) => block.threadId === threadId).map(({ id }) => id));
|
|
6585
7050
|
const currentEventIds = new Set(memory.listEvents().filter((event) => currentBlockIds.has(event.sourceBlockId)).map(({ id }) => id));
|
|
6586
7051
|
const longTermEvents = events.filter((event) => !currentEventIds.has(event.id)).slice(0, AUTO_EVENT_LIMIT);
|
|
6587
|
-
|
|
6588
|
-
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);
|
|
6589
7054
|
}
|
|
6590
7055
|
/**
|
|
6591
7056
|
* Replace the just-sealed DSH turns with one native surface message. The raw
|
|
@@ -6704,7 +7169,8 @@ var StrataGateRuntime = class {
|
|
|
6704
7169
|
}
|
|
6705
7170
|
async prepareFeedback(session, input) {
|
|
6706
7171
|
const namespace = this.namespaceFor(session);
|
|
6707
|
-
const
|
|
7172
|
+
const previous = this.loadFeedbackDraft(namespace);
|
|
7173
|
+
const draft = normalizeFeedbackDraft(input, previous);
|
|
6708
7174
|
this.saveFeedbackDraft(namespace, draft);
|
|
6709
7175
|
const feedbackUrl = feedbackDraftUrl(namespace, this.feedbackOrigin());
|
|
6710
7176
|
return {
|
|
@@ -7370,29 +7836,56 @@ var StrataGateRuntime = class {
|
|
|
7370
7836
|
async searchGraph(session, query, limit = 8) {
|
|
7371
7837
|
await this.flush();
|
|
7372
7838
|
const results = await (await this.space(session)).searchGraphNodes(query, limit);
|
|
7373
|
-
return this.batch(session, results.map(({ node }) => ({
|
|
7839
|
+
return this.batch(session, results.map(({ node, provenanceEventIds }) => ({
|
|
7374
7840
|
ref: `graph-node:${node.id}`,
|
|
7375
7841
|
target: {
|
|
7376
|
-
eventIds:
|
|
7842
|
+
eventIds: (provenanceEventIds ?? []).slice(0, GRAPH_PROVENANCE_LIMIT),
|
|
7377
7843
|
elementIds: [],
|
|
7378
7844
|
citation: citation("graph", node.id, node.name, `graph-node:${node.id}`, "nodeId")
|
|
7379
7845
|
}
|
|
7380
|
-
})), results.map(
|
|
7846
|
+
})), results.map(compactGraphNode));
|
|
7381
7847
|
}
|
|
7382
7848
|
async expandGraphNode(session, id) {
|
|
7383
7849
|
await this.flush();
|
|
7384
7850
|
const memory = await this.space(session);
|
|
7385
7851
|
const node = memory.listGraphNodes().find((candidate) => candidate.id === id);
|
|
7386
7852
|
if (!node) throw new Error(`Unknown graph node: ${id}`);
|
|
7387
|
-
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;
|
|
7388
7871
|
return this.batch(session, [{
|
|
7389
7872
|
ref: `graph-node:${node.id}:expanded`,
|
|
7390
7873
|
target: {
|
|
7391
|
-
eventIds:
|
|
7874
|
+
eventIds: provenanceEventIds,
|
|
7392
7875
|
elementIds: [],
|
|
7393
|
-
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 })
|
|
7394
7877
|
}
|
|
7395
|
-
}], {
|
|
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
|
+
});
|
|
7396
7889
|
}
|
|
7397
7890
|
scheduleBlockDerivation(session, memory) {
|
|
7398
7891
|
const threadId = String(session.id);
|
|
@@ -7704,18 +8197,27 @@ function compactEvent(event, score) {
|
|
|
7704
8197
|
scoreMeaning: "Ranking-only BM25/RRF score; not confidence, probability, or factual accuracy."
|
|
7705
8198
|
};
|
|
7706
8199
|
}
|
|
7707
|
-
function compactGraphNode(
|
|
8200
|
+
function compactGraphNode(result) {
|
|
8201
|
+
const { node, score, matchedFields, matchReason } = result;
|
|
7708
8202
|
return {
|
|
7709
8203
|
id: node.id,
|
|
7710
8204
|
name: node.name,
|
|
7711
8205
|
type: node.type,
|
|
7712
8206
|
aliases: node.aliases.map((alias) => compactText2(alias, 160)),
|
|
8207
|
+
...node.tags ? { tags: node.tags.map((tag) => compactText2(tag, 120)) } : {},
|
|
7713
8208
|
currentState: compactText2(node.currentState, 500),
|
|
7714
8209
|
status: node.status,
|
|
7715
8210
|
rankScore: score,
|
|
7716
8211
|
...matchedFields ? { matchedFields } : {},
|
|
7717
8212
|
...matchReason ? { matchReason } : {},
|
|
7718
|
-
|
|
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 } : {}
|
|
7719
8221
|
};
|
|
7720
8222
|
}
|
|
7721
8223
|
function compactRawHit(result) {
|
|
@@ -7790,7 +8292,7 @@ function activatedElements(memory, relevance) {
|
|
|
7790
8292
|
weight
|
|
7791
8293
|
]).map(({ item }) => item);
|
|
7792
8294
|
}
|
|
7793
|
-
function renderActivatedMemory(events,
|
|
8295
|
+
function renderActivatedMemory(events, graphResults) {
|
|
7794
8296
|
const heading = [
|
|
7795
8297
|
"[Activated long-term memory]",
|
|
7796
8298
|
"Historical memory context.",
|
|
@@ -7819,13 +8321,23 @@ Events:
|
|
|
7819
8321
|
tokens += cost;
|
|
7820
8322
|
eventCount += 1;
|
|
7821
8323
|
}
|
|
7822
|
-
for (const
|
|
8324
|
+
for (const result of graphResults) {
|
|
8325
|
+
const node = result.node;
|
|
7823
8326
|
const rendered = JSON.stringify({
|
|
7824
8327
|
nodeId: node.id,
|
|
7825
8328
|
name: node.name,
|
|
7826
8329
|
type: node.type,
|
|
8330
|
+
aliases: node.aliases,
|
|
8331
|
+
tags: node.tags,
|
|
8332
|
+
status: node.status,
|
|
8333
|
+
metadataEvidenceStatus: result.metadataEvidenceStatus,
|
|
8334
|
+
matchType: result.matchType,
|
|
7827
8335
|
currentState: node.currentState,
|
|
7828
|
-
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
|
|
7829
8341
|
});
|
|
7830
8342
|
const cost = estimateTokens(`
|
|
7831
8343
|
KnowledgeGraph:
|
|
@@ -7903,7 +8415,7 @@ function registerMemoryTools(ctx, runtime) {
|
|
|
7903
8415
|
}));
|
|
7904
8416
|
ctx.tools.register(defineTool({
|
|
7905
8417
|
name: "memory_search_graph",
|
|
7906
|
-
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.",
|
|
7907
8419
|
parameters: {
|
|
7908
8420
|
query: { type: "string", required: true },
|
|
7909
8421
|
limit: { type: "integer", description: "Maximum results, 1-20." }
|
|
@@ -7913,7 +8425,7 @@ function registerMemoryTools(ctx, runtime) {
|
|
|
7913
8425
|
}));
|
|
7914
8426
|
ctx.tools.register(defineTool({
|
|
7915
8427
|
name: "memory_expand_graph_node",
|
|
7916
|
-
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.",
|
|
7917
8429
|
parameters: { id: { type: "string", required: true } },
|
|
7918
8430
|
output: jsonOutput,
|
|
7919
8431
|
execute: async (args, exec) => runtime.expandGraphNode(sessionOf(exec), args.id)
|
|
@@ -9037,17 +9549,36 @@ async function sources(runtime, url) {
|
|
|
9037
9549
|
events = [event];
|
|
9038
9550
|
ids = new Set(event.sourceMessageIds);
|
|
9039
9551
|
} else if (nodeId) {
|
|
9040
|
-
const
|
|
9041
|
-
if (!
|
|
9042
|
-
|
|
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));
|
|
9043
9572
|
ids = new Set(events.flatMap(({ sourceMessageIds }) => sourceMessageIds));
|
|
9044
|
-
const edges = snapshot.graphEdges.filter(({ fromNodeId, toNodeId }) => fromNodeId === node.id || toNodeId === node.id);
|
|
9045
|
-
const relatedNodeIds = /* @__PURE__ */ new Set([node.id, ...edges.flatMap(({ fromNodeId, toNodeId }) => [fromNodeId, toNodeId])]);
|
|
9046
9573
|
return {
|
|
9047
9574
|
namespace,
|
|
9048
|
-
node,
|
|
9049
|
-
nodes
|
|
9575
|
+
node: view.node,
|
|
9576
|
+
nodes,
|
|
9050
9577
|
edges,
|
|
9578
|
+
currentFacts: view.currentFacts,
|
|
9579
|
+
historicalFacts: view.historicalFacts,
|
|
9580
|
+
currentEdges: view.currentEdges,
|
|
9581
|
+
historicalEdges: view.historicalEdges,
|
|
9051
9582
|
events: events.map((event) => eventSummary(event)),
|
|
9052
9583
|
messages: sourceMessages(snapshot, ids)
|
|
9053
9584
|
};
|
|
@@ -9081,7 +9612,7 @@ async function sources(runtime, url) {
|
|
|
9081
9612
|
namespace,
|
|
9082
9613
|
events: events.map((event) => eventSummary(event, eventId ? snapshot : void 0)),
|
|
9083
9614
|
...eventId ? {
|
|
9084
|
-
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 }))
|
|
9085
9616
|
} : {},
|
|
9086
9617
|
elements: elements.map(elementSummary),
|
|
9087
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 })),
|