MCPcopy Create free account
hub / github.com/block/buzz / buildMemoryGraph

Function buildMemoryGraph

desktop/src/features/agent-memory/lib/buildMemoryGraph.ts:56–121  ·  view source on GitHub ↗
(listing: AgentMemoryListing)

Source from the content-addressed store, hash-verified

54 * facing signal ("this memory used to exist but doesn't anymore").
55 */
56export function buildMemoryGraph(listing: AgentMemoryListing): MemoryGraph {
57 // slug → entry, used for both reachability and dangling-ref detection.
58 // `core` lives in this index too so that a `[[core]]` ref from a memory
59 // back-references correctly (rare but possible).
60 const bySlug = new Map<string, EngramEntry>();
61 if (listing.core) bySlug.set(listing.core.slug, listing.core);
62 for (const m of listing.memories) bySlug.set(m.slug, m);
63
64 // Track which entries we've already placed in the tree.
65 const visited = new Set<string>();
66 // refSlug → list of slugs that referenced it. Built lazily as we discover
67 // refs that don't resolve.
68 const danglingMap = new Map<string, string[]>();
69
70 function recordDangling(refSlug: string, referencedBy: string): void {
71 const list = danglingMap.get(refSlug);
72 if (list) {
73 // Don't double-count if the same body refs the same dangling slug
74 // twice — duplicate `[[foo]] [[foo]]` should still surface a single
75 // referrer in the UI.
76 if (!list.includes(referencedBy)) list.push(referencedBy);
77 } else {
78 danglingMap.set(refSlug, [referencedBy]);
79 }
80 }
81
82 function buildNode(entry: EngramEntry): MemoryTreeNode {
83 visited.add(entry.slug);
84 const children: MemoryTreeNode[] = [];
85 for (const refSlug of entry.outgoingRefs) {
86 const target = bySlug.get(refSlug);
87 if (!target) {
88 recordDangling(refSlug, entry.slug);
89 continue;
90 }
91 // Skip self-refs and already-visited nodes; the visited guard makes
92 // the recursion finite even on cyclic graphs.
93 if (visited.has(target.slug)) continue;
94 children.push(buildNode(target));
95 }
96 return { entry, children };
97 }
98
99 const rootedTree = listing.core ? buildNode(listing.core) : null;
100
101 // Orphans = every memory not visited during the BFS from core. Sort by
102 // slug so the UI is deterministic across refetches.
103 const orphans = listing.memories
104 .filter((m) => !visited.has(m.slug))
105 .slice()
106 .sort((a, b) => a.slug.localeCompare(b.slug));
107
108 // Even orphans can themselves cite refs we should surface. Walk them
109 // (no tree, just ref resolution) so dangling targets are complete.
110 for (const o of orphans) {
111 for (const refSlug of o.outgoingRefs) {
112 if (!bySlug.has(refSlug)) recordDangling(refSlug, o.slug);
113 }

Callers 2

useAgentMemoryGraphFunction · 0.90

Calls 5

buildNodeFunction · 0.85
recordDanglingFunction · 0.85
setMethod · 0.80
hasMethod · 0.80
fromMethod · 0.45

Tested by

no test coverage detected