(sdk: ISdk, kv: StateKV)
| 5 | import { recordAudit } from "./audit.js"; |
| 6 | |
| 7 | export function registerCascadeFunction(sdk: ISdk, kv: StateKV): void { |
| 8 | sdk.registerFunction("mem::cascade-update", |
| 9 | async (data: { supersededMemoryId: string }) => { |
| 10 | if (!data.supersededMemoryId || typeof data.supersededMemoryId !== "string") { |
| 11 | return { success: false, error: "supersededMemoryId is required" }; |
| 12 | } |
| 13 | |
| 14 | const superseded = await kv.get<Memory>(KV.memories, data.supersededMemoryId); |
| 15 | if (!superseded) { |
| 16 | return { success: false, error: "superseded memory not found" }; |
| 17 | } |
| 18 | |
| 19 | let flaggedNodes = 0; |
| 20 | let flaggedEdges = 0; |
| 21 | let flaggedMemories = 0; |
| 22 | |
| 23 | const obsIds = new Set(superseded.sourceObservationIds || []); |
| 24 | |
| 25 | if (obsIds.size > 0) { |
| 26 | const now = new Date().toISOString(); |
| 27 | const nodes = await kv.list<GraphNode>(KV.graphNodes); |
| 28 | for (const node of nodes) { |
| 29 | if (node.stale) continue; |
| 30 | const overlap = (node.sourceObservationIds ?? []).some((id) => obsIds.has(id)); |
| 31 | if (overlap) { |
| 32 | node.stale = true; |
| 33 | node.updatedAt = now; |
| 34 | await kv.set(KV.graphNodes, node.id, node); |
| 35 | await recordAudit(kv, "consolidate", "mem::cascade-update", [node.id], { |
| 36 | resourceType: "GraphNode", |
| 37 | change: "marked stale from superseded memory", |
| 38 | supersededMemoryId: data.supersededMemoryId, |
| 39 | }); |
| 40 | flaggedNodes++; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | const edges = await kv.list<GraphEdge>(KV.graphEdges); |
| 45 | for (const edge of edges) { |
| 46 | if (edge.stale) continue; |
| 47 | const overlap = (edge.sourceObservationIds ?? []).some((id) => obsIds.has(id)); |
| 48 | if (overlap) { |
| 49 | edge.stale = true; |
| 50 | await kv.set(KV.graphEdges, edge.id, edge); |
| 51 | await recordAudit(kv, "consolidate", "mem::cascade-update", [edge.id], { |
| 52 | resourceType: "GraphEdge", |
| 53 | change: "marked stale from superseded memory", |
| 54 | supersededMemoryId: data.supersededMemoryId, |
| 55 | }); |
| 56 | flaggedEdges++; |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | const supersededConcepts = new Set( |
| 62 | (superseded.concepts ?? []).map((c) => c.toLowerCase()), |
| 63 | ); |
| 64 | if (supersededConcepts.size >= 2) { |
no test coverage detected