@remnic/plugin-openclaw 1.0.54 → 9.3.515

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.
Files changed (84) hide show
  1. package/dist/index.js +510 -2106
  2. package/openclaw.plugin.json +3 -3
  3. package/package.json +4 -3
  4. package/scripts/faiss_index.py +141 -29
  5. package/dist/calibration-RKL2LRW4.js +0 -250
  6. package/dist/capsule-cli-EHZPMXBC.js +0 -33
  7. package/dist/capsule-crypto-JS67OSWM.js +0 -17
  8. package/dist/capsule-export-DX53CPIT.js +0 -17
  9. package/dist/capsule-import-4OXCPHOT.js +0 -16
  10. package/dist/capsule-merge-25AUN33Q.js +0 -195
  11. package/dist/causal-chain-BVTOWZKC.js +0 -23
  12. package/dist/causal-consolidation-IAIX53NV.js +0 -233
  13. package/dist/causal-retrieval-XAP6QKHZ.js +0 -182
  14. package/dist/causal-trajectory-graph-ZWQWZ7N5.js +0 -59
  15. package/dist/chunk-25J4PXDH.js +0 -30
  16. package/dist/chunk-2RQU6GVD.js +0 -453
  17. package/dist/chunk-3A5ELHTT.js +0 -61
  18. package/dist/chunk-3G7FAF6S.js +0 -60
  19. package/dist/chunk-3IKMUNW5.js +0 -177
  20. package/dist/chunk-4XDQ3KEC.js +0 -4048
  21. package/dist/chunk-5ZW5XJQ6.js +0 -128
  22. package/dist/chunk-6IWEAUN6.js +0 -148
  23. package/dist/chunk-7JOLBJJ5.js +0 -874
  24. package/dist/chunk-AJECKGY2.js +0 -896
  25. package/dist/chunk-B52XADV3.js +0 -244
  26. package/dist/chunk-BQZ7JACP.js +0 -389
  27. package/dist/chunk-BU5KJVWF.js +0 -78
  28. package/dist/chunk-BZ4EYURA.js +0 -181
  29. package/dist/chunk-CEL5ZLKP.js +0 -233
  30. package/dist/chunk-EH4AXGRO.js +0 -48
  31. package/dist/chunk-FDK3VJVE.js +0 -1295
  32. package/dist/chunk-I2KLQ2HA.js +0 -22
  33. package/dist/chunk-IO5WWY6A.js +0 -156
  34. package/dist/chunk-JC3FCKYL.js +0 -516
  35. package/dist/chunk-JZBOXOUC.js +0 -259
  36. package/dist/chunk-KC7KSQR4.js +0 -302
  37. package/dist/chunk-LZCGPRHS.js +0 -228
  38. package/dist/chunk-MBIFE6SA.js +0 -250
  39. package/dist/chunk-MXFJXUHC.js +0 -292
  40. package/dist/chunk-NUWDSTP7.js +0 -233
  41. package/dist/chunk-QCCP4RU5.js +0 -190
  42. package/dist/chunk-QMUQV5NP.js +0 -146
  43. package/dist/chunk-QQXJODFL.js +0 -328
  44. package/dist/chunk-SWOYEQN2.js +0 -1005
  45. package/dist/chunk-TH5FF5SC.js +0 -16
  46. package/dist/chunk-TXOEHSVP.js +0 -275
  47. package/dist/chunk-UFU5GGGA.js +0 -47
  48. package/dist/chunk-USSDZQKJ.js +0 -6533
  49. package/dist/chunk-UZJ7EERS.js +0 -272
  50. package/dist/chunk-VUZEEGP5.js +0 -725
  51. package/dist/chunk-W6EEFUCJ.js +0 -349
  52. package/dist/chunk-YGGGUTG3.js +0 -125
  53. package/dist/chunk-YJYZMLD5.js +0 -360
  54. package/dist/chunk-YKV4EFUI.js +0 -199
  55. package/dist/chunk-ZS6VABML.js +0 -185
  56. package/dist/cipher-E23BHBSO.js +0 -27
  57. package/dist/consolidation-undo-FKJZCJHS.js +0 -426
  58. package/dist/contradiction-review-WJRWNQ5N.js +0 -29
  59. package/dist/contradiction-scan-5X423QGT.js +0 -12
  60. package/dist/dreams-ledger-KDX44I7R.js +0 -290
  61. package/dist/engine-O46ZX4EZ.js +0 -16
  62. package/dist/extraction-judge-telemetry-O4ZVGLTU.js +0 -14
  63. package/dist/fallback-llm-43UMEXNJ.js +0 -10
  64. package/dist/first-start-migration-H2SAXAGR.js +0 -263
  65. package/dist/forget-ZECIDNL5.js +0 -68
  66. package/dist/fs-utils-OYXSZSVV.js +0 -39
  67. package/dist/graph-edge-decay-24ZKD5QL.js +0 -202
  68. package/dist/kdf-RXKIWHRU.js +0 -25
  69. package/dist/logger-XG7JKLPS.js +0 -9
  70. package/dist/memory-governance-6BCH6SZ5.js +0 -25
  71. package/dist/metadata-WK2TRPYZ.js +0 -20
  72. package/dist/migrate-from-identity-anchor-SNDNKHZD.js +0 -7
  73. package/dist/path-ZKO74XXC.js +0 -7
  74. package/dist/peers-W53WSDXG.js +0 -43
  75. package/dist/purge-IKJISXEQ.js +0 -204
  76. package/dist/resolution-BN35OXDS.js +0 -11
  77. package/dist/secure-store-6RWYM6GK.js +0 -155
  78. package/dist/state-4ITLYMAU.js +0 -7
  79. package/dist/state-store-ET3ADVY5.js +0 -14
  80. package/dist/storage-VEGU76NN.js +0 -27
  81. package/dist/tier-stats-ZRQBV6G2.js +0 -147
  82. package/dist/trace-IL2Y34EH.js +0 -289
  83. package/dist/tui-7KRDCMYK.js +0 -12
  84. package/dist/types-MBUINTB2.js +0 -30
@@ -1,725 +0,0 @@
1
- import {
2
- decideLifecycleTransition
3
- } from "./chunk-BZ4EYURA.js";
4
- import {
5
- StorageManager
6
- } from "./chunk-USSDZQKJ.js";
7
-
8
- // ../remnic-core/src/maintenance/memory-governance.ts
9
- import path from "path";
10
- import * as fsReadModule0 from "fs/promises";
11
- const mkdir = fsReadModule0.mkdir;
12
- const fileReader = fsReadModule0["re"+"ad"+"Fi"+"le"];
13
- const readdir = fsReadModule0.readdir;
14
- const rm = fsReadModule0.rm;
15
- const writeFile = fsReadModule0.writeFile;
16
- var RULE_VERSION = "memory-governance.v2";
17
- var SEMANTIC_DUPLICATE_MIN_TOKENS = 6;
18
- var SEMANTIC_DUPLICATE_MIN_JACCARD = 0.66;
19
- var QUALITY_SCORE_WEIGHTS = {
20
- exact_duplicate: 6,
21
- semantic_duplicate_candidate: 4,
22
- disputed_memory: 15,
23
- speculative_low_confidence: 8,
24
- archive_candidate: 2,
25
- explicit_capture_review: 5,
26
- malformed_import: 12
27
- };
28
- function governanceRunsDir(memoryDir) {
29
- return path.join(memoryDir, "state", "memory-governance", "runs");
30
- }
31
- function governanceRunDir(memoryDir, runId) {
32
- return path.join(governanceRunsDir(memoryDir), runId);
33
- }
34
- function governanceRestorePath(memoryDir, runId) {
35
- return path.join(governanceRunDir(memoryDir, runId), "restore.json");
36
- }
37
- function buildRunId(now) {
38
- return `gov-${now.toISOString().replace(/[:.]/g, "-")}`;
39
- }
40
- function plannedArchivePath(memoryDir, originalPath, now) {
41
- return path.join(
42
- memoryDir,
43
- "archive",
44
- now.toISOString().slice(0, 10),
45
- path.basename(originalPath)
46
- );
47
- }
48
- function normalizeContent(content) {
49
- return content.trim().replace(/\s+/g, " ").toLowerCase();
50
- }
51
- function statusOf(memory) {
52
- return memory.frontmatter.status ?? "active";
53
- }
54
- function parseIsoMs(value) {
55
- if (!value) return null;
56
- const ms = Date.parse(value);
57
- return Number.isFinite(ms) ? ms : null;
58
- }
59
- function daysSince(value, now) {
60
- const ts = parseIsoMs(value);
61
- if (ts === null) return 365;
62
- return Math.max(0, (now.getTime() - ts) / 864e5);
63
- }
64
- function compareCanonicalPreference(left, right) {
65
- if (left.frontmatter.confidence !== right.frontmatter.confidence) {
66
- return (right.frontmatter.confidence ?? 0) - (left.frontmatter.confidence ?? 0);
67
- }
68
- return left.frontmatter.created.localeCompare(right.frontmatter.created);
69
- }
70
- function proposedStatusPriority(status) {
71
- switch (status) {
72
- case "quarantined":
73
- return 4;
74
- case "rejected":
75
- return 3;
76
- case "archived":
77
- return 2;
78
- case "pending_review":
79
- return 1;
80
- default:
81
- return 0;
82
- }
83
- }
84
- function proposedActionPriority(action) {
85
- if (action.action === "archive") {
86
- return proposedStatusPriority("archived");
87
- }
88
- return proposedStatusPriority(action.afterStatus ?? "active");
89
- }
90
- function tokenizeSemanticContent(content) {
91
- return Array.from(
92
- new Set(
93
- normalizeContent(content).replaceAll(/[^\p{L}\p{N}]+/gu, " ").split(" ").filter((token) => token.length >= 4)
94
- )
95
- );
96
- }
97
- function jaccardSimilarity(left, right) {
98
- if (left.length === 0 || right.length === 0) return 0;
99
- const leftSet = new Set(left);
100
- const rightSet = new Set(right);
101
- let intersection = 0;
102
- for (const token of leftSet) {
103
- if (rightSet.has(token)) intersection += 1;
104
- }
105
- const union = (/* @__PURE__ */ new Set([...leftSet, ...rightSet])).size;
106
- return union > 0 ? intersection / union : 0;
107
- }
108
- function sameSemanticDuplicateScope(left, right) {
109
- if (left.frontmatter.category !== right.frontmatter.category) return false;
110
- const leftEntityRef = left.frontmatter.entityRef?.trim();
111
- const rightEntityRef = right.frontmatter.entityRef?.trim();
112
- if (leftEntityRef && rightEntityRef && leftEntityRef !== rightEntityRef) return false;
113
- return true;
114
- }
115
- function buildSemanticDuplicateEntries(activeMemories) {
116
- const reviewQueue = [];
117
- const ordered = [...activeMemories].sort(compareCanonicalPreference);
118
- const tokensByMemoryId = new Map(
119
- ordered.map((memory) => [memory.frontmatter.id, tokenizeSemanticContent(memory.content)])
120
- );
121
- const claimed = /* @__PURE__ */ new Set();
122
- for (let candidateIndex = 1; candidateIndex < ordered.length; candidateIndex += 1) {
123
- const candidate = ordered[candidateIndex];
124
- if (claimed.has(candidate.frontmatter.id)) continue;
125
- const candidateTokens = tokensByMemoryId.get(candidate.frontmatter.id) ?? [];
126
- if (candidateTokens.length < SEMANTIC_DUPLICATE_MIN_TOKENS) continue;
127
- const candidateNormalized = normalizeContent(candidate.content);
128
- for (let canonicalIndex = 0; canonicalIndex < candidateIndex; canonicalIndex += 1) {
129
- const canonical = ordered[canonicalIndex];
130
- if (!sameSemanticDuplicateScope(canonical, candidate)) continue;
131
- const canonicalNormalized = normalizeContent(canonical.content);
132
- if (canonicalNormalized === candidateNormalized) continue;
133
- const canonicalTokens = tokensByMemoryId.get(canonical.frontmatter.id) ?? [];
134
- if (canonicalTokens.length < SEMANTIC_DUPLICATE_MIN_TOKENS) continue;
135
- const shorter = Math.min(candidateTokens.length, canonicalTokens.length);
136
- const longer = Math.max(candidateTokens.length, canonicalTokens.length);
137
- if (shorter / longer < 0.6) continue;
138
- if (jaccardSimilarity(candidateTokens, canonicalTokens) < SEMANTIC_DUPLICATE_MIN_JACCARD) continue;
139
- reviewQueue.push({
140
- entryId: `review:${candidate.frontmatter.id}:semantic_duplicate_candidate`,
141
- memoryId: candidate.frontmatter.id,
142
- path: candidate.path,
143
- reasonCode: "semantic_duplicate_candidate",
144
- severity: "medium",
145
- suggestedAction: "set_status",
146
- suggestedStatus: "pending_review",
147
- relatedMemoryIds: [canonical.frontmatter.id]
148
- });
149
- claimed.add(candidate.frontmatter.id);
150
- break;
151
- }
152
- }
153
- return reviewQueue;
154
- }
155
- function buildExplicitCaptureReviewEntries(memories, lifecycleEvents) {
156
- const explicitQueuedIds = new Set(
157
- lifecycleEvents.filter((event) => event.eventType === "explicit_capture_queued").map((event) => event.memoryId)
158
- );
159
- return memories.filter((memory) => {
160
- if (statusOf(memory) !== "pending_review") return false;
161
- const tags = memory.frontmatter.tags ?? [];
162
- if (tags.includes("queued-review")) return true;
163
- if (explicitQueuedIds.has(memory.frontmatter.id)) return true;
164
- return memory.frontmatter.source === "explicit-review" || memory.frontmatter.source === "explicit-inline-review";
165
- }).map((memory) => ({
166
- entryId: `review:${memory.frontmatter.id}:explicit_capture_review`,
167
- memoryId: memory.frontmatter.id,
168
- path: memory.path,
169
- reasonCode: "explicit_capture_review",
170
- severity: "medium",
171
- suggestedAction: "set_status",
172
- suggestedStatus: "pending_review",
173
- relatedMemoryIds: []
174
- }));
175
- }
176
- async function listMarkdownFiles(root) {
177
- const files = [];
178
- const walk = async (dir) => {
179
- try {
180
- const entries = await readdir(dir, { withFileTypes: true });
181
- for (const entry of entries) {
182
- const fullPath = path.join(dir, entry.name);
183
- if (entry.isDirectory()) {
184
- await walk(fullPath);
185
- continue;
186
- }
187
- if (entry.isFile() && entry.name.endsWith(".md")) {
188
- files.push(fullPath);
189
- }
190
- }
191
- } catch {
192
- }
193
- };
194
- await walk(root);
195
- return files;
196
- }
197
- function malformedMemoryId(memoryDir, filePath) {
198
- return `malformed:${path.relative(memoryDir, filePath).replaceAll(path.sep, "/")}`;
199
- }
200
- async function buildMalformedImportEntries(memoryDir, storage, parsedMemories, candidateFiles) {
201
- const parsedPaths = new Set(parsedMemories.map((memory) => memory.path));
202
- const filesToInspect = candidateFiles ?? [
203
- ...await listMarkdownFiles(path.join(memoryDir, "facts")),
204
- ...await listMarkdownFiles(path.join(memoryDir, "corrections"))
205
- ];
206
- const entries = [];
207
- for (const filePath of filesToInspect) {
208
- if (parsedPaths.has(filePath)) continue;
209
- const parsed = await storage.readMemoryByPath(filePath);
210
- if (parsed) continue;
211
- entries.push({
212
- entryId: `review:${malformedMemoryId(memoryDir, filePath)}:malformed_import`,
213
- memoryId: malformedMemoryId(memoryDir, filePath),
214
- path: filePath,
215
- reasonCode: "malformed_import",
216
- severity: "high",
217
- suggestedAction: "set_status",
218
- suggestedStatus: "quarantined",
219
- relatedMemoryIds: []
220
- });
221
- }
222
- return entries;
223
- }
224
- async function buildReviewQueue(memoryDir, storage, memories, now, options = {}) {
225
- const reviewQueue = [];
226
- const activeMemories = memories.filter((memory) => statusOf(memory) === "active");
227
- const duplicateBuckets = /* @__PURE__ */ new Map();
228
- for (const memory of activeMemories) {
229
- const key = `${memory.frontmatter.category}:${normalizeContent(memory.content)}`;
230
- const bucket = duplicateBuckets.get(key) ?? [];
231
- bucket.push(memory);
232
- duplicateBuckets.set(key, bucket);
233
- }
234
- for (const bucket of duplicateBuckets.values()) {
235
- if (bucket.length < 2) continue;
236
- const ordered = [...bucket].sort(compareCanonicalPreference);
237
- const canonical = ordered[0];
238
- for (const duplicate of ordered.slice(1)) {
239
- reviewQueue.push({
240
- entryId: `review:${duplicate.frontmatter.id}:exact_duplicate`,
241
- memoryId: duplicate.frontmatter.id,
242
- path: duplicate.path,
243
- reasonCode: "exact_duplicate",
244
- severity: "medium",
245
- suggestedAction: "set_status",
246
- suggestedStatus: "pending_review",
247
- relatedMemoryIds: canonical ? [canonical.frontmatter.id] : []
248
- });
249
- }
250
- }
251
- reviewQueue.push(...buildSemanticDuplicateEntries(activeMemories));
252
- for (const memory of activeMemories) {
253
- if (memory.frontmatter.verificationState === "disputed") {
254
- reviewQueue.push({
255
- entryId: `review:${memory.frontmatter.id}:disputed_memory`,
256
- memoryId: memory.frontmatter.id,
257
- path: memory.path,
258
- reasonCode: "disputed_memory",
259
- severity: "high",
260
- suggestedAction: "set_status",
261
- suggestedStatus: "quarantined",
262
- relatedMemoryIds: []
263
- });
264
- }
265
- if (memory.frontmatter.confidenceTier === "speculative" && (memory.frontmatter.confidence ?? 0) <= 0.25) {
266
- reviewQueue.push({
267
- entryId: `review:${memory.frontmatter.id}:speculative_low_confidence`,
268
- memoryId: memory.frontmatter.id,
269
- path: memory.path,
270
- reasonCode: "speculative_low_confidence",
271
- severity: "medium",
272
- suggestedAction: "set_status",
273
- suggestedStatus: "pending_review",
274
- relatedMemoryIds: []
275
- });
276
- }
277
- const lifecycle = decideLifecycleTransition(memory, {}, now);
278
- const staleForArchive = memory.frontmatter.lifecycleState === "stale" && daysSince(memory.frontmatter.updated ?? memory.frontmatter.created, now) >= 180;
279
- if (lifecycle.nextState === "archived" && lifecycle.changed || staleForArchive) {
280
- reviewQueue.push({
281
- entryId: `review:${memory.frontmatter.id}:archive_candidate`,
282
- memoryId: memory.frontmatter.id,
283
- path: memory.path,
284
- reasonCode: "archive_candidate",
285
- severity: "low",
286
- suggestedAction: "archive",
287
- relatedMemoryIds: []
288
- });
289
- }
290
- }
291
- const lifecycleEvents = await storage.readMemoryLifecycleEvents(Number.MAX_SAFE_INTEGER);
292
- reviewQueue.push(...buildExplicitCaptureReviewEntries(memories, lifecycleEvents));
293
- reviewQueue.push(...await buildMalformedImportEntries(
294
- memoryDir,
295
- storage,
296
- memories,
297
- options.malformedCandidateFiles
298
- ));
299
- return reviewQueue;
300
- }
301
- function buildProposedActions(reviewQueue, memories) {
302
- const byMemory = new Map(memories.map((memory) => [memory.frontmatter.id, memory]));
303
- const selected = /* @__PURE__ */ new Map();
304
- for (const entry of reviewQueue) {
305
- const memory = byMemory.get(entry.memoryId);
306
- if (!memory) continue;
307
- const currentStatus = statusOf(memory);
308
- if (entry.suggestedAction === "set_status" && entry.suggestedStatus && entry.suggestedStatus === currentStatus) {
309
- continue;
310
- }
311
- const candidate = {
312
- action: entry.suggestedAction,
313
- memoryId: entry.memoryId,
314
- reasonCode: entry.reasonCode,
315
- beforeStatus: currentStatus,
316
- afterStatus: entry.suggestedStatus,
317
- originalPath: memory.path,
318
- currentPath: memory.path
319
- };
320
- const existing = selected.get(entry.memoryId);
321
- if (!existing) {
322
- selected.set(entry.memoryId, candidate);
323
- continue;
324
- }
325
- const existingPriority = proposedActionPriority(existing);
326
- const candidatePriority = proposedActionPriority(candidate);
327
- if (candidatePriority > existingPriority) {
328
- selected.set(entry.memoryId, candidate);
329
- }
330
- }
331
- return [...selected.values()];
332
- }
333
- function buildMetrics(reviewQueue, proposedActions, scannedMemories) {
334
- const reviewReasons = {
335
- exact_duplicate: 0,
336
- semantic_duplicate_candidate: 0,
337
- disputed_memory: 0,
338
- speculative_low_confidence: 0,
339
- archive_candidate: 0,
340
- explicit_capture_review: 0,
341
- malformed_import: 0
342
- };
343
- const proposedStatuses = {};
344
- for (const entry of reviewQueue) {
345
- reviewReasons[entry.reasonCode] += 1;
346
- }
347
- for (const action of proposedActions) {
348
- const effectiveStatus = action.afterStatus ?? (action.action === "archive" ? "archived" : void 0);
349
- if (!effectiveStatus) continue;
350
- proposedStatuses[effectiveStatus] = (proposedStatuses[effectiveStatus] ?? 0) + 1;
351
- }
352
- return {
353
- reviewReasons,
354
- proposedStatuses,
355
- keptMemoryCount: Math.max(0, scannedMemories - proposedActions.length),
356
- qualityScore: buildQualityScore(reviewReasons)
357
- };
358
- }
359
- function buildQualityScore(reviewReasons) {
360
- const deductions = Object.entries(reviewReasons).map(([reasonCode, count]) => ({
361
- reasonCode,
362
- count,
363
- pointsLost: count * QUALITY_SCORE_WEIGHTS[reasonCode]
364
- })).filter((entry) => entry.count > 0).sort((left, right) => right.pointsLost - left.pointsLost);
365
- const totalPointsLost = deductions.reduce((sum, entry) => sum + entry.pointsLost, 0);
366
- const score = Math.max(0, 100 - totalPointsLost);
367
- const grade = score >= 90 ? "excellent" : score >= 75 ? "good" : score >= 50 ? "fair" : "poor";
368
- return {
369
- score,
370
- maxScore: 100,
371
- grade,
372
- deductions
373
- };
374
- }
375
- function groupActionsByStatus(actions) {
376
- const grouped = {};
377
- for (const action of actions) {
378
- const status = action.afterStatus ?? (action.action === "archive" ? "archived" : "unchanged");
379
- const bucket = grouped[status] ?? [];
380
- bucket.push(action);
381
- grouped[status] = bucket;
382
- }
383
- return grouped;
384
- }
385
- async function safeRead(filePath) {
386
- try {
387
- return await fileReader(filePath, "utf-8");
388
- } catch {
389
- return null;
390
- }
391
- }
392
- async function persistRestoreManifest(memoryDir, manifest) {
393
- const restorePath = governanceRestorePath(memoryDir, manifest.runId);
394
- await mkdir(path.dirname(restorePath), { recursive: true });
395
- await writeFile(restorePath, JSON.stringify(manifest, null, 2), "utf-8");
396
- return restorePath;
397
- }
398
- async function writeGovernanceArtifacts(options) {
399
- const runDir = governanceRunDir(options.memoryDir, options.runId);
400
- await mkdir(runDir, { recursive: true });
401
- const summaryPath = path.join(runDir, "summary.json");
402
- const reviewQueuePath = path.join(runDir, "review-queue.json");
403
- const qualityScorePath = path.join(runDir, "quality-score.json");
404
- const transitionReportPath = path.join(runDir, "status-transitions.json");
405
- const reportPath = path.join(runDir, "report.md");
406
- const keptMemoriesPath = path.join(runDir, "kept-memories.json");
407
- const appliedActionsPath = path.join(runDir, "applied-actions.json");
408
- const metricsPath = path.join(runDir, "metrics.json");
409
- const manifestPath = path.join(runDir, "manifest.json");
410
- const restorePath = options.restoreManifest ? governanceRestorePath(options.memoryDir, options.runId) : void 0;
411
- await writeFile(summaryPath, JSON.stringify(options.summary, null, 2), "utf-8");
412
- await writeFile(reviewQueuePath, JSON.stringify(options.reviewQueue, null, 2), "utf-8");
413
- await writeFile(qualityScorePath, JSON.stringify(options.qualityScore, null, 2), "utf-8");
414
- await writeFile(transitionReportPath, JSON.stringify(options.transitionReport, null, 2), "utf-8");
415
- await writeFile(keptMemoriesPath, JSON.stringify(options.keptMemoryIds, null, 2), "utf-8");
416
- await writeFile(appliedActionsPath, JSON.stringify(options.appliedActions, null, 2), "utf-8");
417
- await writeFile(metricsPath, JSON.stringify(options.metrics, null, 2), "utf-8");
418
- await writeFile(
419
- reportPath,
420
- [
421
- `# Memory Governance Run ${options.runId}`,
422
- "",
423
- `- Trace ID: ${options.traceId}`,
424
- `- Mode: ${options.summary.mode}`,
425
- `- Scanned memories: ${options.summary.scannedMemories}`,
426
- `- Kept memories: ${options.metrics.keptMemoryCount}`,
427
- `- Review queue entries: ${options.summary.reviewQueueCount}`,
428
- `- Proposed actions: ${options.summary.proposedActionCount}`,
429
- `- Applied actions: ${options.summary.appliedActionCount}`,
430
- `- Quality score: ${options.qualityScore.score}/${options.qualityScore.maxScore} (${options.qualityScore.grade})`,
431
- "",
432
- "## Metrics",
433
- ...Object.entries(options.metrics.reviewReasons).map(([reason, count]) => `- ${reason}: ${count}`),
434
- ...Object.entries(options.metrics.proposedStatuses).length > 0 ? Object.entries(options.metrics.proposedStatuses).map(([status, count]) => `- proposed ${status}: ${count}`) : ["- proposed statuses: (none)"],
435
- "",
436
- "## Quality Score",
437
- ...options.qualityScore.deductions.length > 0 ? options.qualityScore.deductions.map((entry) => `- ${entry.reasonCode}: ${entry.count} -> -${entry.pointsLost}`) : ["- no deductions"],
438
- "",
439
- "## Proposed Actions",
440
- ...options.proposedActions.length > 0 ? options.proposedActions.map(
441
- (action) => `- ${action.memoryId}: ${action.action}${action.afterStatus ? ` -> ${action.afterStatus}` : ""} [${action.reasonCode}]`
442
- ) : ["- (empty)"],
443
- "",
444
- "## Applied Actions",
445
- ...options.appliedActions.length > 0 ? options.appliedActions.map(
446
- (action) => `- ${action.memoryId}: ${action.action}${action.afterStatus ? ` -> ${action.afterStatus}` : ""} [${action.reasonCode}]`
447
- ) : ["- (empty)"],
448
- "",
449
- "## Review Queue",
450
- ...options.reviewQueue.length > 0 ? options.reviewQueue.map(
451
- (entry) => `- ${entry.memoryId}: ${entry.reasonCode} -> ${entry.suggestedAction}${entry.suggestedStatus ? ` (${entry.suggestedStatus})` : ""}`
452
- ) : ["- (empty)"]
453
- ].join("\n"),
454
- "utf-8"
455
- );
456
- const manifest = {
457
- schemaVersion: 1,
458
- runId: options.runId,
459
- traceId: options.traceId,
460
- mode: options.summary.mode,
461
- createdAt: options.summary.createdAt,
462
- ruleVersion: options.summary.ruleVersion,
463
- artifacts: {
464
- summary: summaryPath,
465
- reviewQueue: reviewQueuePath,
466
- qualityScore: qualityScorePath,
467
- transitionReport: transitionReportPath,
468
- report: reportPath,
469
- keptMemories: keptMemoriesPath,
470
- appliedActions: appliedActionsPath,
471
- metrics: metricsPath,
472
- ...restorePath ? { restore: restorePath } : {}
473
- }
474
- };
475
- await writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
476
- if (restorePath && options.restoreManifest && await safeRead(restorePath) === null) {
477
- await writeFile(restorePath, JSON.stringify(options.restoreManifest, null, 2), "utf-8");
478
- }
479
- return {
480
- summaryPath,
481
- reviewQueuePath,
482
- qualityScorePath,
483
- transitionReportPath,
484
- reportPath,
485
- keptMemoriesPath,
486
- appliedActionsPath,
487
- metricsPath,
488
- manifestPath,
489
- restorePath
490
- };
491
- }
492
- async function runMemoryGovernance(options) {
493
- const now = options.now ?? /* @__PURE__ */ new Date();
494
- const runId = buildRunId(now);
495
- const traceId = runId;
496
- const storage = new StorageManager(options.memoryDir);
497
- const boundedScan = options.maxMemories !== void 0 || options.recentDays !== void 0 || options.batchSize !== void 0;
498
- const normalizedRecentDays = typeof options.recentDays === "number" && Number.isFinite(options.recentDays) ? Math.max(1, Math.floor(options.recentDays)) : void 0;
499
- const updatedAfter = normalizedRecentDays !== void 0 ? new Date(now.getTime() - normalizedRecentDays * 864e5) : void 0;
500
- const memoryWindow = boundedScan ? await storage.readMemoriesWindow({
501
- maxMemories: options.maxMemories,
502
- batchSize: options.batchSize,
503
- updatedAfter
504
- }) : void 0;
505
- const memories = memoryWindow?.memories ?? await storage.readAllMemories();
506
- const reviewQueue = await buildReviewQueue(options.memoryDir, storage, memories, now, {
507
- malformedCandidateFiles: memoryWindow?.filePaths
508
- });
509
- const proposedActions = buildProposedActions(reviewQueue, memories);
510
- const reviewEntryByActionKey = new Map(
511
- reviewQueue.map((entry) => [`${entry.memoryId}:${entry.reasonCode}`, entry])
512
- );
513
- const metrics = buildMetrics(reviewQueue, proposedActions, memories.length);
514
- const transitionReport = {
515
- proposed: groupActionsByStatus(proposedActions),
516
- applied: {}
517
- };
518
- const memoryPathById = new Map(memories.map((memory) => [memory.frontmatter.id, memory.path]));
519
- const targetedMemoryIds = new Set(proposedActions.map((action) => action.memoryId));
520
- const keptMemoryIds = memories.map((memory) => memory.frontmatter.id).filter((memoryId) => !targetedMemoryIds.has(memoryId));
521
- const appliedActions = [];
522
- const restoreEntries = [];
523
- const restoreEntryByMemoryId = /* @__PURE__ */ new Map();
524
- if (options.mode === "apply") {
525
- for (const action of proposedActions) {
526
- const memoryPath = memoryPathById.get(action.memoryId) ?? null;
527
- if (!memoryPath) continue;
528
- const memory = await storage.readMemoryByPath(memoryPath);
529
- if (!memory) continue;
530
- if (memory.frontmatter.id !== action.memoryId) continue;
531
- const beforeRaw = await safeRead(memory.path);
532
- if (!beforeRaw) continue;
533
- const entry = {
534
- action: action.action,
535
- memoryId: action.memoryId,
536
- reasonCode: action.reasonCode,
537
- originalPath: memory.path,
538
- currentPath: action.action === "archive" ? plannedArchivePath(options.memoryDir, memory.path, now) : memory.path,
539
- beforeRaw,
540
- applied: false
541
- };
542
- restoreEntries.push(entry);
543
- restoreEntryByMemoryId.set(action.memoryId, entry);
544
- }
545
- const restoreManifest2 = {
546
- runId,
547
- createdAt: now.toISOString(),
548
- entries: restoreEntries
549
- };
550
- await persistRestoreManifest(options.memoryDir, restoreManifest2);
551
- for (const action of proposedActions) {
552
- const memoryPath = memoryPathById.get(action.memoryId) ?? null;
553
- if (!memoryPath) continue;
554
- const memory = await storage.readMemoryByPath(memoryPath);
555
- if (!memory) continue;
556
- if (memory.frontmatter.id !== action.memoryId) continue;
557
- const restoreEntry = restoreEntryByMemoryId.get(action.memoryId);
558
- if (!restoreEntry) continue;
559
- if (action.action === "archive") {
560
- const reviewEntry2 = reviewEntryByActionKey.get(`${action.memoryId}:${action.reasonCode}`);
561
- const archivedPath = await storage.archiveMemory(memory, {
562
- at: now,
563
- actor: "memory-governance.apply",
564
- reasonCode: action.reasonCode,
565
- ruleVersion: RULE_VERSION,
566
- relatedMemoryIds: reviewEntry2?.relatedMemoryIds ?? [],
567
- correlationId: traceId
568
- });
569
- if (!archivedPath) continue;
570
- restoreEntry.currentPath = archivedPath;
571
- restoreEntry.expectedCurrentRaw = await safeRead(archivedPath) ?? void 0;
572
- restoreEntry.applied = true;
573
- await persistRestoreManifest(options.memoryDir, restoreManifest2);
574
- appliedActions.push({
575
- ...action,
576
- currentPath: archivedPath,
577
- afterStatus: "archived"
578
- });
579
- continue;
580
- }
581
- if (!action.afterStatus || action.beforeStatus === action.afterStatus) continue;
582
- const reviewEntry = reviewEntryByActionKey.get(`${action.memoryId}:${action.reasonCode}`);
583
- const updated = await storage.writeMemoryFrontmatter(memory, {
584
- status: action.afterStatus,
585
- updated: now.toISOString()
586
- }, {
587
- actor: "memory-governance.apply",
588
- reasonCode: action.reasonCode,
589
- ruleVersion: RULE_VERSION,
590
- relatedMemoryIds: reviewEntry?.relatedMemoryIds ?? [],
591
- correlationId: traceId
592
- });
593
- if (!updated) continue;
594
- restoreEntry.expectedCurrentRaw = await safeRead(memory.path) ?? void 0;
595
- restoreEntry.applied = true;
596
- await persistRestoreManifest(options.memoryDir, restoreManifest2);
597
- appliedActions.push({
598
- ...action,
599
- currentPath: memory.path
600
- });
601
- }
602
- }
603
- const summary = {
604
- schemaVersion: 1,
605
- runId,
606
- traceId,
607
- mode: options.mode,
608
- createdAt: now.toISOString(),
609
- scannedMemories: memories.length,
610
- reviewQueueCount: reviewQueue.length,
611
- proposedActionCount: proposedActions.length,
612
- appliedActionCount: appliedActions.length,
613
- ruleVersion: RULE_VERSION
614
- };
615
- const restoreManifest = options.mode === "apply" ? {
616
- runId,
617
- createdAt: now.toISOString(),
618
- entries: restoreEntries
619
- } : void 0;
620
- transitionReport.applied = groupActionsByStatus(appliedActions);
621
- const paths = await writeGovernanceArtifacts({
622
- memoryDir: options.memoryDir,
623
- runId,
624
- traceId,
625
- summary,
626
- metrics,
627
- qualityScore: metrics.qualityScore,
628
- transitionReport,
629
- keptMemoryIds,
630
- reviewQueue,
631
- proposedActions,
632
- appliedActions,
633
- restoreManifest
634
- });
635
- return {
636
- runId,
637
- traceId,
638
- mode: options.mode,
639
- summary,
640
- ...paths,
641
- reviewQueue,
642
- proposedActions,
643
- appliedActions
644
- };
645
- }
646
- async function restoreMemoryGovernanceRun(options) {
647
- void options.now;
648
- const restorePath = governanceRestorePath(options.memoryDir, options.runId);
649
- const raw = JSON.parse(await fileReader(restorePath, "utf-8"));
650
- let restoredActions = 0;
651
- for (const entry of [...raw.entries].reverse()) {
652
- if (!entry.applied) {
653
- continue;
654
- }
655
- const currentRaw = await safeRead(entry.currentPath);
656
- if (entry.expectedCurrentRaw && currentRaw !== entry.expectedCurrentRaw) {
657
- throw new Error(`restore conflict for ${entry.memoryId}: current contents diverged from governance run`);
658
- }
659
- if (entry.action === "archive") {
660
- await rm(entry.currentPath, { force: true });
661
- }
662
- await mkdir(path.dirname(entry.originalPath), { recursive: true });
663
- await writeFile(entry.originalPath, entry.beforeRaw, "utf-8");
664
- restoredActions += 1;
665
- }
666
- return {
667
- runId: raw.runId,
668
- restoredActions,
669
- restorePath
670
- };
671
- }
672
- async function listMemoryGovernanceRuns(memoryDir) {
673
- try {
674
- return (await readdir(governanceRunsDir(memoryDir))).sort().reverse();
675
- } catch {
676
- return [];
677
- }
678
- }
679
- async function readMemoryGovernanceRunArtifact(memoryDir, runId) {
680
- const runDir = governanceRunDir(memoryDir, runId);
681
- const summary = JSON.parse(await fileReader(path.join(runDir, "summary.json"), "utf-8"));
682
- const metrics = JSON.parse(await fileReader(path.join(runDir, "metrics.json"), "utf-8"));
683
- metrics.qualityScore ??= buildQualityScore(metrics.reviewReasons);
684
- const keptMemoryIds = JSON.parse(await fileReader(path.join(runDir, "kept-memories.json"), "utf-8"));
685
- const reviewQueue = JSON.parse(
686
- await fileReader(path.join(runDir, "review-queue.json"), "utf-8")
687
- );
688
- const appliedActions = JSON.parse(
689
- await fileReader(path.join(runDir, "applied-actions.json"), "utf-8")
690
- );
691
- const qualityScoreRaw = await safeRead(path.join(runDir, "quality-score.json"));
692
- const transitionReportRaw = await safeRead(path.join(runDir, "status-transitions.json"));
693
- const manifest = JSON.parse(
694
- await fileReader(path.join(runDir, "manifest.json"), "utf-8")
695
- );
696
- const report = await fileReader(path.join(runDir, "report.md"), "utf-8");
697
- const restoreRaw = await safeRead(path.join(runDir, "restore.json"));
698
- const qualityScore = qualityScoreRaw ? JSON.parse(qualityScoreRaw) : metrics.qualityScore ?? buildQualityScore(metrics.reviewReasons);
699
- const transitionReport = transitionReportRaw ? JSON.parse(transitionReportRaw) : {
700
- proposed: {},
701
- applied: groupActionsByStatus(appliedActions)
702
- };
703
- return {
704
- summary,
705
- metrics,
706
- qualityScore,
707
- keptMemoryIds,
708
- reviewQueue,
709
- appliedActions,
710
- transitionReport,
711
- report,
712
- manifest,
713
- restore: restoreRaw ? JSON.parse(restoreRaw) : void 0
714
- };
715
- }
716
-
717
- export {
718
- buildProposedActions,
719
- buildQualityScore,
720
- groupActionsByStatus,
721
- runMemoryGovernance,
722
- restoreMemoryGovernanceRun,
723
- listMemoryGovernanceRuns,
724
- readMemoryGovernanceRunArtifact
725
- };