@vibeiao/sdk 0.1.25 → 0.1.30

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.
@@ -0,0 +1,675 @@
1
+ // src/compoundingMemory.ts
2
+ import { promises as fs } from "fs";
3
+ import path from "path";
4
+ import { createHash } from "crypto";
5
+ var COMPOUNDING_MEMORY_VERSION = 1;
6
+ var META_FILE = ".vibeiao-compounding-memory.json";
7
+ var UPGRADE_MARKER = "<!-- vibeiao:compounding-memory-upgrade -->";
8
+ var resolveRoot = (root = ".") => path.resolve(root);
9
+ var asDateKey = (date, timeZone = "UTC") => {
10
+ const parts = new Intl.DateTimeFormat("en-CA", {
11
+ timeZone,
12
+ year: "numeric",
13
+ month: "2-digit",
14
+ day: "2-digit"
15
+ }).formatToParts(date);
16
+ const year = parts.find((p) => p.type === "year")?.value;
17
+ const month = parts.find((p) => p.type === "month")?.value;
18
+ const day = parts.find((p) => p.type === "day")?.value;
19
+ if (!year || !month || !day) {
20
+ return date.toISOString().slice(0, 10);
21
+ }
22
+ return `${year}-${month}-${day}`;
23
+ };
24
+ var ensureDir = async (dir) => {
25
+ await fs.mkdir(dir, { recursive: true });
26
+ };
27
+ var readIfExists = async (filePath) => {
28
+ try {
29
+ return await fs.readFile(filePath, "utf-8");
30
+ } catch {
31
+ return null;
32
+ }
33
+ };
34
+ var ensureFile = async (filePath, content, result) => {
35
+ try {
36
+ await fs.access(filePath);
37
+ result.existed.push(filePath);
38
+ return false;
39
+ } catch {
40
+ await ensureDir(path.dirname(filePath));
41
+ await fs.writeFile(filePath, content, "utf-8");
42
+ result.created.push(filePath);
43
+ return true;
44
+ }
45
+ };
46
+ var appendBlockIfMissing = async (filePath, marker, block, result) => {
47
+ const current = await readIfExists(filePath) ?? "";
48
+ if (current.includes(marker)) return false;
49
+ const next = `${current.trimEnd()}
50
+
51
+ ${block.trim()}
52
+ `;
53
+ await fs.writeFile(filePath, next, "utf-8");
54
+ result.touched.push(filePath);
55
+ return true;
56
+ };
57
+ var ensureHeading = async (filePath, heading, defaultBody, result) => {
58
+ const current = await readIfExists(filePath) ?? "";
59
+ if (current.includes(`## ${heading}`)) return false;
60
+ const block = `## ${heading}
61
+ ${defaultBody.trim()}
62
+ `;
63
+ const next = `${current.trimEnd()}
64
+
65
+ ${block}`;
66
+ await fs.writeFile(filePath, `${next.trimEnd()}
67
+ `, "utf-8");
68
+ result.touched.push(filePath);
69
+ return true;
70
+ };
71
+ var buildWorkingStateTemplate = () => `# WORKING_STATE.md \u2014 Minimal Always-Loaded State
72
+
73
+ Purpose: keep the agent working set bounded while memory compounds on disk.
74
+
75
+ ## Now (1\u20133 bullets)
76
+ - Keep current priorities explicit and small.
77
+
78
+ ## Active threads (queue)
79
+ - Move details into project dossiers and daily logs.
80
+
81
+ ## Next actions (do next)
82
+ - Keep this file concise; prefer pointers over prose.
83
+
84
+ ## Constraints / defaults (operational)
85
+ - Timezone default for scheduling/interpretation.
86
+ - Avoid duplicate notifications.
87
+
88
+ ## Pointers (where the truth lives)
89
+ - Curated memory: MEMORY.md
90
+ - Daily logs: memory/YYYY-MM-DD.md
91
+ - Graph memory: memory/graph/*
92
+ - Projects: PROJECTS.md + projects/*
93
+ `;
94
+ var buildMemoryTemplate = () => `# MEMORY.md \u2014 Long-Term Memory (Curated)
95
+
96
+ This file stores stable, durable facts. Keep details in daily logs.
97
+
98
+ ## User Archetype / Preferences
99
+ - Name:
100
+ - Timezone:
101
+ - Communication style:
102
+
103
+ ## Identity / Directives (Assistant)
104
+ - Name:
105
+ - Vibe:
106
+
107
+ ## Active Systems / Automation
108
+ -
109
+
110
+ ## Project Status (Current)
111
+ -
112
+
113
+ ## Lessons Learned / Working Conventions
114
+ - Text > Brain: if it matters, write it down.
115
+ - Keep WORKING_STATE.md bounded.
116
+
117
+ ## Sensitive / Do-Not-Guess
118
+ - Confirm before external sends/posts/payments/deletions/production actions.
119
+ `;
120
+ var buildProjectsTemplate = () => `# PROJECTS.md \u2014 Active Project Index
121
+
122
+ Use this as the pointer index. Keep each project's detail in projects/<name>/.
123
+
124
+ ## Active
125
+ - Example: projects/example/{STATE,DECISIONS,RUNBOOKS,BACKLOG}.md
126
+ `;
127
+ var buildDailyLedgerTemplate = (dateKey) => `# ${dateKey}
128
+
129
+ ## Context
130
+ -
131
+
132
+ ## Decisions
133
+ -
134
+
135
+ ## Commands / Changes
136
+ -
137
+
138
+ ## Preferences
139
+ -
140
+
141
+ ## Open Questions / Next
142
+ -
143
+ `;
144
+ var buildMemoryReadme = () => `# memory/ \u2014 Daily Ledger
145
+
146
+ This folder is the ground-truth log.
147
+
148
+ Related: WORKING_STATE.md in workspace root is the bounded working set (what matters now).
149
+
150
+ ## Format
151
+ - Files named YYYY-MM-DD.md
152
+ - Capture context, decisions, commands/changes, preferences, and next actions.
153
+
154
+ ## Rule
155
+ - If it should survive restarts, write it to a file.
156
+ `;
157
+ var buildGraphReadme = () => `# memory/graph \u2014 Phase 0.5 (Write-enabled, evidence-gated)
158
+
159
+ This directory stores the local memory knowledge graph for retrieval reliability.
160
+
161
+ ## Files
162
+ - schema.v1.json \u2014 constraints
163
+ - policy.v1.json \u2014 merge/apply policy
164
+ - nodes.jsonl / edges.jsonl \u2014 graph store
165
+ - review_queue.jsonl \u2014 unresolved proposals
166
+ - checkpoints/ \u2014 rollback snapshots
167
+ - proposals/ \u2014 validation/apply artifacts
168
+ - GOLDEN_QUERIES_V1.md \u2014 retrieval regression prompts
169
+
170
+ ## Rules
171
+ - Require source evidence refs.
172
+ - Keep duplicate/conflict checks green.
173
+ - Run golden-query regression after extractor policy changes.
174
+ `;
175
+ var buildGoldenQueries = () => `# Memory Graph Golden Queries (v1)
176
+
177
+ 1) User timezone preference
178
+ 2) Notification dedupe preference
179
+ 3) Current active project priorities
180
+ 4) Safety constraints for external actions
181
+ 5) Most recent high-impact decision
182
+
183
+ Acceptance: each query should return at least one relevant graph node with evidence refs.
184
+ `;
185
+ var GRAPH_SCHEMA = `{
186
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
187
+ "title": "Memory Graph Schema v1",
188
+ "version": "v1",
189
+ "node": {
190
+ "required": ["id", "type", "name", "summary", "status", "created_at", "updated_at", "source_refs", "confidence"],
191
+ "types": ["person", "project", "system", "artifact", "decision", "preference", "constraint", "event"]
192
+ },
193
+ "edge": {
194
+ "required": ["from", "to", "type", "weight", "evidence_refs", "created_at", "updated_at", "confidence", "state"],
195
+ "types": ["related_to", "depends_on", "decided_by", "supersedes", "blocked_by", "enables", "prefers", "conflicts_with"],
196
+ "states": ["active", "soft_deleted"]
197
+ },
198
+ "policy": {
199
+ "mode": "phase0.5",
200
+ "evidence_required": true,
201
+ "auto_apply": true
202
+ }
203
+ }
204
+ `;
205
+ var GRAPH_POLICY = `{
206
+ "version": "v1",
207
+ "mode": "apply",
208
+ "auto_apply": true,
209
+ "min_confidence_node": 0.9,
210
+ "min_confidence_edge": 0.92,
211
+ "require_evidence": true,
212
+ "conflict_policy": "queue",
213
+ "duplicate_policy": "replace",
214
+ "max_apply_per_run": 50,
215
+ "rollback": {
216
+ "enabled": true,
217
+ "retain_checkpoints": 30
218
+ }
219
+ }
220
+ `;
221
+ var writeVersionMeta = async (root, version) => {
222
+ const filePath = path.join(root, META_FILE);
223
+ const payload = {
224
+ version,
225
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
226
+ };
227
+ await fs.writeFile(filePath, JSON.stringify(payload, null, 2), "utf-8");
228
+ };
229
+ var readCompoundingMemoryVersion = async (root = ".") => {
230
+ try {
231
+ const raw = await fs.readFile(path.join(resolveRoot(root), META_FILE), "utf-8");
232
+ const parsed = JSON.parse(raw);
233
+ const version = Number(parsed?.version);
234
+ return Number.isFinite(version) ? version : 0;
235
+ } catch {
236
+ return 0;
237
+ }
238
+ };
239
+ var upgradeCompoundingMemorySystem = async (options = {}) => {
240
+ const root = resolveRoot(options.root);
241
+ const includeGraph = options.includeGraph !== false;
242
+ const includeProjects = options.includeProjects !== false;
243
+ const appendUpgradeNote = options.appendUpgradeNote !== false;
244
+ const timeZone = options.timeZone || "UTC";
245
+ const dateKey = asDateKey(options.now ?? /* @__PURE__ */ new Date(), timeZone);
246
+ await ensureDir(root);
247
+ const result = {
248
+ root,
249
+ memoryDir: path.join(root, "memory"),
250
+ graphDir: includeGraph ? path.join(root, "memory", "graph") : null,
251
+ projectsDir: includeProjects ? path.join(root, "projects") : null,
252
+ dateKey,
253
+ dailyLedgerPath: path.join(root, "memory", `${dateKey}.md`),
254
+ created: [],
255
+ touched: [],
256
+ existed: [],
257
+ changed: false,
258
+ version: COMPOUNDING_MEMORY_VERSION
259
+ };
260
+ await ensureFile(path.join(root, "WORKING_STATE.md"), buildWorkingStateTemplate(), result);
261
+ await ensureFile(path.join(root, "MEMORY.md"), buildMemoryTemplate(), result);
262
+ await ensureFile(path.join(root, "PROJECTS.md"), buildProjectsTemplate(), result);
263
+ await ensureFile(path.join(root, "memory", "README.md"), buildMemoryReadme(), result);
264
+ await ensureFile(result.dailyLedgerPath, buildDailyLedgerTemplate(dateKey), result);
265
+ if (includeProjects) {
266
+ await ensureDir(path.join(root, "projects"));
267
+ await ensureFile(path.join(root, "projects", ".gitkeep"), "", result);
268
+ }
269
+ if (includeGraph) {
270
+ const graphRoot = path.join(root, "memory", "graph");
271
+ await ensureDir(graphRoot);
272
+ await ensureDir(path.join(graphRoot, "checkpoints"));
273
+ await ensureDir(path.join(graphRoot, "proposals"));
274
+ await ensureFile(path.join(graphRoot, "README.md"), buildGraphReadme(), result);
275
+ await ensureFile(path.join(graphRoot, "schema.v1.json"), GRAPH_SCHEMA, result);
276
+ await ensureFile(path.join(graphRoot, "policy.v1.json"), GRAPH_POLICY, result);
277
+ await ensureFile(path.join(graphRoot, "nodes.jsonl"), "", result);
278
+ await ensureFile(path.join(graphRoot, "edges.jsonl"), "", result);
279
+ await ensureFile(path.join(graphRoot, "review_queue.jsonl"), "", result);
280
+ await ensureFile(path.join(graphRoot, "audit.log"), "", result);
281
+ await ensureFile(path.join(graphRoot, "GOLDEN_QUERIES_V1.md"), buildGoldenQueries(), result);
282
+ }
283
+ await ensureHeading(
284
+ path.join(root, "MEMORY.md"),
285
+ "Lessons Learned / Working Conventions",
286
+ "- Keep WORKING_STATE.md bounded and current.\n- Distill stable facts here; keep detail in daily logs.",
287
+ result
288
+ );
289
+ await ensureHeading(
290
+ path.join(root, "MEMORY.md"),
291
+ "Sensitive / Do-Not-Guess",
292
+ "- Confirm before external actions (posts, payments, deletions, production ops).",
293
+ result
294
+ );
295
+ await ensureHeading(
296
+ path.join(root, "WORKING_STATE.md"),
297
+ "Pointers (where the truth lives)",
298
+ "- Curated memory: MEMORY.md\n- Daily ledger: memory/YYYY-MM-DD.md\n- Memory graph: memory/graph/*\n- Project index: PROJECTS.md",
299
+ result
300
+ );
301
+ if (appendUpgradeNote) {
302
+ await appendBlockIfMissing(
303
+ result.dailyLedgerPath,
304
+ UPGRADE_MARKER,
305
+ `${UPGRADE_MARKER}
306
+ - Initialized/upgraded compounding memory scaffold (v${COMPOUNDING_MEMORY_VERSION}).`,
307
+ result
308
+ );
309
+ }
310
+ await writeVersionMeta(root, COMPOUNDING_MEMORY_VERSION);
311
+ result.changed = result.created.length > 0 || result.touched.length > 0;
312
+ return result;
313
+ };
314
+ var DEFAULT_WORKING_STATE_MAX_LINES = 200;
315
+ var DEFAULT_WORKING_STATE_MAX_CHARS = 12e3;
316
+ var DEFAULT_WORKING_STATE_MAX_OVERFLOW_CHARS = 8e3;
317
+ var DEFAULT_BACKUP_RETENTION = 14;
318
+ var DEFAULT_REQUIRED_SET_INTERVAL_MS = 24 * 60 * 60 * 1e3;
319
+ var REQUIRED_GRAPH_FILES = [
320
+ "README.md",
321
+ "schema.v1.json",
322
+ "policy.v1.json",
323
+ "nodes.jsonl",
324
+ "edges.jsonl",
325
+ "review_queue.jsonl",
326
+ "audit.log",
327
+ "GOLDEN_QUERIES_V1.md"
328
+ ];
329
+ var asIsoStamp = (date) => date.toISOString();
330
+ var countLines = (text) => {
331
+ if (!text) return 0;
332
+ return text.replace(/\n$/, "").split("\n").length;
333
+ };
334
+ var selectBackupCandidates = async (root) => {
335
+ const candidates = [
336
+ ".vibeiao-compounding-memory.json",
337
+ "WORKING_STATE.md",
338
+ "MEMORY.md",
339
+ "PROJECTS.md"
340
+ ];
341
+ const files = [];
342
+ for (const rel of candidates) {
343
+ const full = path.join(root, rel);
344
+ try {
345
+ const stat = await fs.stat(full);
346
+ if (stat.isFile()) files.push(rel);
347
+ } catch {
348
+ }
349
+ }
350
+ const walk = async (dirRel) => {
351
+ const fullDir = path.join(root, dirRel);
352
+ let entries;
353
+ try {
354
+ entries = await fs.readdir(fullDir, { withFileTypes: true });
355
+ } catch {
356
+ return;
357
+ }
358
+ for (const entry of entries) {
359
+ const rel = path.join(dirRel, String(entry.name));
360
+ if (entry.isDirectory()) {
361
+ await walk(rel);
362
+ } else if (entry.isFile()) {
363
+ files.push(rel);
364
+ }
365
+ }
366
+ };
367
+ await walk("memory");
368
+ await walk("projects");
369
+ return Array.from(new Set(files)).sort();
370
+ };
371
+ var ensureTextFile = async (filePath) => {
372
+ const content = await fs.readFile(filePath, "utf-8");
373
+ return content;
374
+ };
375
+ var resolveBackupDir = (root, backupDir) => backupDir ? path.resolve(backupDir) : path.join(root, "memory", ".backup", "compounding-memory");
376
+ var appendDailyLedgerNote = async (root, timeZone, now, title, body) => {
377
+ const dateKey = asDateKey(now, timeZone);
378
+ const ledgerPath = path.join(root, "memory", `${dateKey}.md`);
379
+ const stamp = asIsoStamp(now);
380
+ const block = `
381
+ ### ${stamp} ${title}
382
+ ${body.trim()}
383
+ `;
384
+ await ensureDir(path.dirname(ledgerPath));
385
+ await fs.appendFile(ledgerPath, block, "utf-8");
386
+ return ledgerPath;
387
+ };
388
+ var enforceWorkingStateDiscipline = async (options = {}) => {
389
+ const timeZone = options.timeZone || "UTC";
390
+ const now = options.now ?? /* @__PURE__ */ new Date();
391
+ const maxLines = Math.max(20, Number(options.maxLines ?? DEFAULT_WORKING_STATE_MAX_LINES));
392
+ const maxChars = Math.max(1e3, Number(options.maxChars ?? DEFAULT_WORKING_STATE_MAX_CHARS));
393
+ const maxOverflowChars = Math.max(256, Number(options.maxOverflowChars ?? DEFAULT_WORKING_STATE_MAX_OVERFLOW_CHARS));
394
+ const upgrade = await upgradeCompoundingMemorySystem({
395
+ root: options.root,
396
+ timeZone,
397
+ now,
398
+ appendUpgradeNote: false
399
+ });
400
+ const root = upgrade.root;
401
+ const filePath = path.join(root, "WORKING_STATE.md");
402
+ const current = await ensureTextFile(filePath);
403
+ const lineCount = countLines(current);
404
+ const charCount = current.length;
405
+ if (lineCount <= maxLines && charCount <= maxChars) {
406
+ return {
407
+ filePath,
408
+ ledgerPath: upgrade.dailyLedgerPath,
409
+ lineCount,
410
+ charCount,
411
+ maxLines,
412
+ maxChars,
413
+ bounded: true,
414
+ trimmed: false,
415
+ movedChars: 0
416
+ };
417
+ }
418
+ let next = current;
419
+ const lines = current.split("\n");
420
+ if (lines.length > maxLines) {
421
+ next = `${lines.slice(0, maxLines).join("\n")}
422
+ `;
423
+ }
424
+ if (next.length > maxChars) {
425
+ next = `${next.slice(0, Math.max(0, maxChars - 1)).trimEnd()}\u2026
426
+ `;
427
+ }
428
+ let overflow = current.slice(next.length).trim();
429
+ if (!overflow && next !== current) {
430
+ overflow = current.replace(next, "").trim();
431
+ }
432
+ const moved = overflow ? overflow.slice(0, maxOverflowChars) : "";
433
+ await fs.writeFile(filePath, next.trimEnd() + "\n", "utf-8");
434
+ let ledgerPath = upgrade.dailyLedgerPath;
435
+ if (moved) {
436
+ ledgerPath = await appendDailyLedgerNote(
437
+ root,
438
+ timeZone,
439
+ now,
440
+ "[working-state-compaction]",
441
+ `- Auto-compacted WORKING_STATE.md to stay within budget (${maxLines} lines / ${maxChars} chars).
442
+ - Overflow snapshot:
443
+
444
+ \`\`\`
445
+ ${moved}
446
+ \`\`\``
447
+ );
448
+ }
449
+ const written = await ensureTextFile(filePath);
450
+ return {
451
+ filePath,
452
+ ledgerPath,
453
+ lineCount: countLines(written),
454
+ charCount: written.length,
455
+ maxLines,
456
+ maxChars,
457
+ bounded: countLines(written) <= maxLines && written.length <= maxChars,
458
+ trimmed: true,
459
+ movedChars: moved.length
460
+ };
461
+ };
462
+ var checkRecallSubstrate = async (options = {}) => {
463
+ const root = resolveRoot(options.root);
464
+ const graphRoot = path.join(root, "memory", "graph");
465
+ const issues = [];
466
+ for (const rel of REQUIRED_GRAPH_FILES) {
467
+ try {
468
+ await fs.access(path.join(graphRoot, rel));
469
+ } catch {
470
+ issues.push(`missing_graph_file:${rel}`);
471
+ }
472
+ }
473
+ let goldenQueryCount = 0;
474
+ try {
475
+ const golden = await fs.readFile(path.join(graphRoot, "GOLDEN_QUERIES_V1.md"), "utf-8");
476
+ goldenQueryCount = golden.split("\n").filter((line) => /^\s*\d+\)/.test(line.trim())).length;
477
+ if (goldenQueryCount < 3) {
478
+ issues.push("golden_queries_too_few");
479
+ }
480
+ } catch {
481
+ issues.push("golden_queries_unreadable");
482
+ }
483
+ let nodeCount = 0;
484
+ let edgeCount = 0;
485
+ try {
486
+ const nodes = await fs.readFile(path.join(graphRoot, "nodes.jsonl"), "utf-8");
487
+ nodeCount = nodes.split("\n").map((x) => x.trim()).filter(Boolean).length;
488
+ } catch {
489
+ issues.push("nodes_unreadable");
490
+ }
491
+ try {
492
+ const edges = await fs.readFile(path.join(graphRoot, "edges.jsonl"), "utf-8");
493
+ edgeCount = edges.split("\n").map((x) => x.trim()).filter(Boolean).length;
494
+ } catch {
495
+ issues.push("edges_unreadable");
496
+ }
497
+ return {
498
+ ok: issues.length === 0,
499
+ goldenQueryCount,
500
+ nodeCount,
501
+ edgeCount,
502
+ issues
503
+ };
504
+ };
505
+ var createCompoundingMemoryBackup = async (options = {}) => {
506
+ const root = resolveRoot(options.root);
507
+ const now = options.now ?? /* @__PURE__ */ new Date();
508
+ const backupDir = resolveBackupDir(root, options.backupDir);
509
+ const retention = Math.max(1, Number(options.retention ?? DEFAULT_BACKUP_RETENTION));
510
+ await ensureDir(backupDir);
511
+ const files = await selectBackupCandidates(root);
512
+ const payloadFiles = [];
513
+ for (const rel of files) {
514
+ const content = await ensureTextFile(path.join(root, rel));
515
+ payloadFiles.push({ path: rel.replace(/\\/g, "/"), content });
516
+ }
517
+ const payload = {
518
+ version: COMPOUNDING_MEMORY_VERSION,
519
+ createdAt: asIsoStamp(now),
520
+ root,
521
+ files: payloadFiles
522
+ };
523
+ const body = JSON.stringify(payload, null, 2);
524
+ const sha256 = createHash("sha256").update(body).digest("hex");
525
+ const stamp = asIsoStamp(now).replace(/[:.]/g, "-");
526
+ const backupPath = path.join(backupDir, `compounding-memory-${stamp}.json`);
527
+ await fs.writeFile(backupPath, body, "utf-8");
528
+ const entries = (await fs.readdir(backupDir)).filter((name) => name.startsWith("compounding-memory-") && name.endsWith(".json")).sort();
529
+ const excess = entries.length - retention;
530
+ if (excess > 0) {
531
+ const toDelete = entries.slice(0, excess);
532
+ await Promise.all(toDelete.map((name) => fs.unlink(path.join(backupDir, name))));
533
+ }
534
+ const stat = await fs.stat(backupPath);
535
+ return {
536
+ backupPath,
537
+ createdAt: payload.createdAt,
538
+ fileCount: payloadFiles.length,
539
+ sizeBytes: stat.size,
540
+ sha256
541
+ };
542
+ };
543
+ var runCompoundingMemoryRestoreDrill = async (options = {}) => {
544
+ const root = resolveRoot(options.root);
545
+ const backup = await createCompoundingMemoryBackup(options);
546
+ const raw = await fs.readFile(backup.backupPath, "utf-8");
547
+ const payload = JSON.parse(raw);
548
+ const files = Array.isArray(payload.files) ? payload.files : [];
549
+ const restoreRoot = path.join(root, "memory", ".restore-drill", Date.now().toString());
550
+ await ensureDir(restoreRoot);
551
+ for (const row of files) {
552
+ if (!row?.path || typeof row.path !== "string" || typeof row.content !== "string") continue;
553
+ const rel = row.path.replace(/^\/+/, "").replace(/\\/g, "/");
554
+ if (!rel || rel.includes("..")) continue;
555
+ const target = path.join(restoreRoot, rel);
556
+ await ensureDir(path.dirname(target));
557
+ await fs.writeFile(target, row.content, "utf-8");
558
+ }
559
+ const checkedFiles = [
560
+ "WORKING_STATE.md",
561
+ "MEMORY.md",
562
+ "PROJECTS.md",
563
+ "memory/README.md",
564
+ "memory/graph/policy.v1.json"
565
+ ];
566
+ for (const rel of checkedFiles) {
567
+ await fs.access(path.join(restoreRoot, rel));
568
+ }
569
+ const restoredFiles = files.length;
570
+ if (!options.keepRestoreRoot) {
571
+ await fs.rm(restoreRoot, { recursive: true, force: true });
572
+ }
573
+ return {
574
+ ok: true,
575
+ backup,
576
+ restoredFiles,
577
+ checkedFiles,
578
+ restoreRoot
579
+ };
580
+ };
581
+ var runCompoundingMemoryRequiredSet = async (options = {}) => {
582
+ const timeZone = options.timeZone || "UTC";
583
+ const now = options.now ?? /* @__PURE__ */ new Date();
584
+ const upgrade = await upgradeCompoundingMemorySystem({
585
+ root: options.root,
586
+ timeZone,
587
+ now
588
+ });
589
+ const workingState = await enforceWorkingStateDiscipline({
590
+ root: upgrade.root,
591
+ timeZone,
592
+ now,
593
+ maxLines: options.workingStateMaxLines,
594
+ maxChars: options.workingStateMaxChars
595
+ });
596
+ const recall = await checkRecallSubstrate({ root: upgrade.root });
597
+ let restoreDrill = null;
598
+ if (options.runBackupRestoreDrill !== false) {
599
+ restoreDrill = await runCompoundingMemoryRestoreDrill({
600
+ root: upgrade.root,
601
+ backupDir: options.backupDir,
602
+ retention: options.backupRetention,
603
+ now
604
+ });
605
+ await appendDailyLedgerNote(
606
+ upgrade.root,
607
+ timeZone,
608
+ now,
609
+ "[restore-drill]",
610
+ `- status: OK
611
+ - backup: ${restoreDrill.backup.backupPath}
612
+ - restoredFiles: ${restoreDrill.restoredFiles}`
613
+ );
614
+ }
615
+ return {
616
+ ok: workingState.bounded && recall.ok && (restoreDrill ? restoreDrill.ok : true),
617
+ root: upgrade.root,
618
+ upgrade,
619
+ workingState,
620
+ recall,
621
+ restoreDrill,
622
+ checkedAt: asIsoStamp(now)
623
+ };
624
+ };
625
+ var createCompoundingMemoryRequiredSetScheduler = (options = {}) => {
626
+ const intervalMs = Math.max(6e4, Number(options.intervalMs ?? DEFAULT_REQUIRED_SET_INTERVAL_MS));
627
+ let timer = null;
628
+ let stopped = false;
629
+ const runOnce = async () => {
630
+ try {
631
+ const result = await runCompoundingMemoryRequiredSet(options);
632
+ if (options.onSuccess) {
633
+ await options.onSuccess(result);
634
+ }
635
+ return result;
636
+ } catch (err) {
637
+ const error = err instanceof Error ? err : new Error("compounding_memory_required_set_failed");
638
+ if (options.onError) {
639
+ await options.onError(error);
640
+ }
641
+ throw error;
642
+ }
643
+ };
644
+ const start = async () => {
645
+ if (timer) return;
646
+ stopped = false;
647
+ await runOnce();
648
+ if (stopped) return;
649
+ timer = setInterval(() => {
650
+ void runOnce();
651
+ }, intervalMs);
652
+ };
653
+ const stop = () => {
654
+ stopped = true;
655
+ if (timer) {
656
+ clearInterval(timer);
657
+ timer = null;
658
+ }
659
+ };
660
+ return { start, stop, runOnce };
661
+ };
662
+ var ensureCompoundingMemorySystem = upgradeCompoundingMemorySystem;
663
+
664
+ export {
665
+ COMPOUNDING_MEMORY_VERSION,
666
+ readCompoundingMemoryVersion,
667
+ upgradeCompoundingMemorySystem,
668
+ enforceWorkingStateDiscipline,
669
+ checkRecallSubstrate,
670
+ createCompoundingMemoryBackup,
671
+ runCompoundingMemoryRestoreDrill,
672
+ runCompoundingMemoryRequiredSet,
673
+ createCompoundingMemoryRequiredSetScheduler,
674
+ ensureCompoundingMemorySystem
675
+ };