synapse-kit 0.0.0-stage → 0.3.1

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/LICENSE +21 -0
  2. package/README.ko.md +129 -0
  3. package/README.md +127 -2
  4. package/dist/causal-memory.json +4581 -0
  5. package/dist/cli-memory.d.ts +5 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +3474 -0
  8. package/dist/compiler/causal-build.d.ts +51 -0
  9. package/dist/compiler/causal-graph.d.ts +6 -0
  10. package/dist/compiler/cli.d.ts +2 -0
  11. package/dist/compiler/cli.js +1299 -0
  12. package/dist/compiler/core/config.d.ts +16 -0
  13. package/dist/compiler/core/engine.d.ts +17 -0
  14. package/dist/compiler/core/reporter.d.ts +5 -0
  15. package/dist/compiler/i18n.d.ts +281 -0
  16. package/dist/compiler/index.d.ts +21 -0
  17. package/dist/compiler/index.js +1983 -0
  18. package/dist/compiler/plugins/cpp.d.ts +19 -0
  19. package/dist/compiler/plugins/csharp.d.ts +13 -0
  20. package/dist/compiler/plugins/go.d.ts +15 -0
  21. package/dist/compiler/plugins/java.d.ts +16 -0
  22. package/dist/compiler/plugins/native.d.ts +12 -0
  23. package/dist/compiler/plugins/nsis.d.ts +8 -0
  24. package/dist/compiler/plugins/python.d.ts +21 -0
  25. package/dist/compiler/plugins/rust.d.ts +9 -0
  26. package/dist/compiler/plugins/schema.d.ts +12 -0
  27. package/dist/compiler/plugins/svelte.d.ts +10 -0
  28. package/dist/compiler/plugins/typescript.d.ts +23 -0
  29. package/dist/compiler/types.d.ts +72 -0
  30. package/dist/compiler/virtual_120b_types.d.ts +18 -0
  31. package/dist/core/config.d.ts +16 -0
  32. package/dist/core/engine.d.ts +17 -0
  33. package/dist/core/reporter.d.ts +5 -0
  34. package/dist/db/index.js +1910 -0
  35. package/dist/i18n.d.ts +281 -0
  36. package/dist/index.d.ts +7 -0
  37. package/dist/index.js +8282 -0
  38. package/dist/memory/causal-benchmark.d.ts +29 -0
  39. package/dist/memory/causal-branch.d.ts +79 -0
  40. package/dist/memory/causal-build.d.ts +51 -0
  41. package/dist/memory/causal-config.d.ts +38 -0
  42. package/dist/memory/causal-context.d.ts +113 -0
  43. package/dist/memory/causal-contexts.json +316 -0
  44. package/dist/memory/causal-graph.d.ts +6 -0
  45. package/dist/memory/causal-graveyard.d.ts +94 -0
  46. package/dist/memory/causal-lens.d.ts +133 -0
  47. package/dist/memory/causal-memory.json +5054 -0
  48. package/dist/memory/causal-migrator.d.ts +11 -0
  49. package/dist/memory/causal-peer.d.ts +53 -0
  50. package/dist/memory/causal-predictor.d.ts +42 -0
  51. package/dist/memory/causal-query.d.ts +21 -0
  52. package/dist/memory/causal-server.d.ts +578 -0
  53. package/dist/memory/causal-store.d.ts +280 -0
  54. package/dist/memory/causal-synthesizer.d.ts +96 -0
  55. package/dist/memory/causal-timeseries.d.ts +92 -0
  56. package/dist/memory/cli.d.ts +13 -0
  57. package/dist/memory/cli.js +5668 -0
  58. package/dist/memory/index.d.ts +13 -0
  59. package/dist/memory/index.js +4451 -0
  60. package/dist/plugins/cpp.d.ts +19 -0
  61. package/dist/plugins/csharp.d.ts +13 -0
  62. package/dist/plugins/go.d.ts +15 -0
  63. package/dist/plugins/java.d.ts +16 -0
  64. package/dist/plugins/native.d.ts +12 -0
  65. package/dist/plugins/nsis.d.ts +8 -0
  66. package/dist/plugins/python.d.ts +21 -0
  67. package/dist/plugins/rust.d.ts +9 -0
  68. package/dist/plugins/schema.d.ts +12 -0
  69. package/dist/plugins/svelte.d.ts +10 -0
  70. package/dist/plugins/typescript.d.ts +23 -0
  71. package/dist/src/cli.d.ts +2 -0
  72. package/dist/src/core/config.d.ts +12 -0
  73. package/dist/src/core/engine.d.ts +9 -0
  74. package/dist/src/core/reporter.d.ts +5 -0
  75. package/dist/src/index.d.ts +13 -0
  76. package/dist/src/memory/causal-graph.d.ts +6 -0
  77. package/dist/src/plugins/native.d.ts +12 -0
  78. package/dist/src/plugins/nsis.d.ts +8 -0
  79. package/dist/src/plugins/rust.d.ts +9 -0
  80. package/dist/src/plugins/schema.d.ts +12 -0
  81. package/dist/src/plugins/svelte.d.ts +10 -0
  82. package/dist/src/types.d.ts +55 -0
  83. package/dist/types.d.ts +72 -0
  84. package/package.json +87 -4
@@ -0,0 +1,4451 @@
1
+ // @bun
2
+ // memory/causal-config.ts
3
+ import { homedir } from "os";
4
+ import { resolve } from "path";
5
+ var CAUSAL_MEMORY_CONFIG = {
6
+ SCHEMA_VERSION: process.env.SYNAPSE_MEMORY_VERSION || "2.1.0",
7
+ GLOBAL_DB_PATH: process.env.SYNAPSE_MEMORY_PATH || resolve(homedir(), ".gemini", "antigravity", "causal_memory.json"),
8
+ LEGACY_BACKUP_PATH: process.env.SYNAPSE_MEMORY_BACKUP_PATH || resolve(homedir(), ".gemini", "antigravity", "memory_backup_20261101.json"),
9
+ LOCAL_DB_PATH: resolve(import.meta.dir, "causal-memory.json"),
10
+ DEFAULT_HOST: process.env.SYNAPSE_MEMORY_HOST || "127.0.0.1",
11
+ DEFAULT_PORT: parseInt(process.env.SYNAPSE_MEMORY_PORT || "4242", 10),
12
+ REMOTE_URL: process.env.SYNAPSE_MEMORY_REMOTE_URL || "",
13
+ REMOTE_TIMEOUT_MS: parseInt(process.env.SYNAPSE_MEMORY_REMOTE_TIMEOUT || "3000", 10),
14
+ MCP_PROTOCOL_VERSION: process.env.SYNAPSE_MCP_VERSION || "2024-11-05",
15
+ MCP_SERVER_NAME: "synapse-causal-memory",
16
+ MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10),
17
+ CONTEXT_DB_PATH: process.env.SYNAPSE_CONTEXT_PATH || resolve(homedir(), ".gemini", "antigravity", "causal_contexts.json"),
18
+ LOCAL_CONTEXT_DB_PATH: resolve(import.meta.dir, "causal-contexts.json"),
19
+ MAX_CHECKPOINTS_RETAINED: parseInt(process.env.SYNAPSE_MAX_CHECKPOINTS || "50", 10),
20
+ DEFAULT_OBSIDIAN_VAULT_PATH: process.env.SYNAPSE_OBSIDIAN_VAULT || resolve(homedir(), "Documents", "Min", "Min"),
21
+ OBSIDIAN_LENS_SUBDIR: process.env.SYNAPSE_LENS_SUBDIR || "Synapse-Mental"
22
+ };
23
+ // memory/causal-store.ts
24
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
25
+ import { dirname } from "path";
26
+ class CausalMemoryStore {
27
+ dbPath;
28
+ database;
29
+ lastMtimeMs = 0;
30
+ alwaysActiveRules = [];
31
+ domainIndex = new Map;
32
+ commandIndex = new Map;
33
+ exactKeywordIndex = new Map;
34
+ exactFileIndex = new Map;
35
+ prefixFileRules = [];
36
+ compiledSearchRules = [];
37
+ titleToRuleMap = new Map;
38
+ idToRuleMap = new Map;
39
+ relationAdjacency = new Map;
40
+ constructor(customPath, initialDatabase) {
41
+ this.dbPath = customPath || (existsSync(CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH) ? CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH : CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH);
42
+ if (initialDatabase) {
43
+ this.database = initialDatabase;
44
+ this.rebuildIndices();
45
+ } else {
46
+ this.database = this.loadDatabase();
47
+ this.rebuildIndices();
48
+ }
49
+ }
50
+ reloadIfNeeded() {
51
+ try {
52
+ if (!existsSync(this.dbPath))
53
+ return false;
54
+ const stat = statSync(this.dbPath);
55
+ if (stat.mtimeMs > this.lastMtimeMs) {
56
+ this.database = this.loadDatabase();
57
+ this.rebuildIndices();
58
+ return true;
59
+ }
60
+ } catch {}
61
+ return false;
62
+ }
63
+ reload() {
64
+ this.database = this.loadDatabase();
65
+ this.rebuildIndices();
66
+ }
67
+ loadFromMemory(db) {
68
+ this.database = db;
69
+ this.rebuildIndices();
70
+ }
71
+ rebuildIndices() {
72
+ this.alwaysActiveRules = [];
73
+ this.domainIndex.clear();
74
+ this.commandIndex.clear();
75
+ this.exactKeywordIndex.clear();
76
+ this.exactFileIndex.clear();
77
+ this.prefixFileRules = [];
78
+ this.compiledSearchRules = [];
79
+ this.titleToRuleMap.clear();
80
+ this.idToRuleMap.clear();
81
+ this.relationAdjacency.clear();
82
+ const addTokenToKeywordIndex = (token, rule) => {
83
+ if (!token)
84
+ return;
85
+ let list = this.exactKeywordIndex.get(token);
86
+ if (!list) {
87
+ list = [];
88
+ this.exactKeywordIndex.set(token, list);
89
+ }
90
+ if (list[list.length - 1] !== rule) {
91
+ list.push(rule);
92
+ }
93
+ };
94
+ for (const rule of this.database.rules) {
95
+ const domainLower = rule.domain.toLowerCase();
96
+ const titleLower = rule.title.toLowerCase();
97
+ const idLower = rule.id.toLowerCase();
98
+ this.titleToRuleMap.set(titleLower, rule);
99
+ this.idToRuleMap.set(idLower, rule);
100
+ let domList = this.domainIndex.get(domainLower);
101
+ if (!domList) {
102
+ domList = [];
103
+ this.domainIndex.set(domainLower, domList);
104
+ }
105
+ domList.push(rule);
106
+ if (rule.triggers.alwaysActive) {
107
+ this.alwaysActiveRules.push(rule);
108
+ }
109
+ if (rule.triggers.commands) {
110
+ for (const cmd of rule.triggers.commands) {
111
+ const cmdLower = cmd.toLowerCase();
112
+ let cmdList = this.commandIndex.get(cmdLower);
113
+ if (!cmdList) {
114
+ cmdList = [];
115
+ this.commandIndex.set(cmdLower, cmdList);
116
+ }
117
+ cmdList.push(rule);
118
+ }
119
+ }
120
+ addTokenToKeywordIndex(titleLower, rule);
121
+ addTokenToKeywordIndex(domainLower, rule);
122
+ for (const part of titleLower.split(/[\s_-]+/)) {
123
+ addTokenToKeywordIndex(part, rule);
124
+ }
125
+ if (rule.triggers.keywords) {
126
+ for (const kw of rule.triggers.keywords) {
127
+ const kwLower = kw.toLowerCase();
128
+ addTokenToKeywordIndex(kwLower, rule);
129
+ for (const sub of kwLower.split(/[\s_-]+/)) {
130
+ addTokenToKeywordIndex(sub, rule);
131
+ }
132
+ }
133
+ }
134
+ if (rule.triggers.files && rule.triggers.files.length > 0) {
135
+ const prefixPatterns = [];
136
+ for (const pattern of rule.triggers.files) {
137
+ const cleaned = (pattern.includes("\\") ? pattern.replaceAll("\\", "/") : pattern).replace(/^\*\*\/|\*\*$|\*$/g, "").replace(/\/+$/, "").toLowerCase();
138
+ if (!cleaned)
139
+ continue;
140
+ let fList = this.exactFileIndex.get(cleaned);
141
+ if (!fList) {
142
+ fList = [];
143
+ this.exactFileIndex.set(cleaned, fList);
144
+ }
145
+ fList.push(rule);
146
+ if (cleaned.endsWith("-") || cleaned.endsWith("_") || cleaned.includes(":")) {
147
+ prefixPatterns.push(cleaned);
148
+ }
149
+ }
150
+ if (prefixPatterns.length > 0) {
151
+ this.prefixFileRules.push({ rule, domainLower, cleanPatterns: prefixPatterns });
152
+ }
153
+ }
154
+ }
155
+ for (const rel of this.database.relations) {
156
+ const fromLower = rel.from.toLowerCase();
157
+ const toLower = rel.to.toLowerCase();
158
+ let fromAdj = this.relationAdjacency.get(fromLower);
159
+ if (!fromAdj) {
160
+ fromAdj = [];
161
+ this.relationAdjacency.set(fromLower, fromAdj);
162
+ }
163
+ fromAdj.push(rel);
164
+ if (toLower !== fromLower) {
165
+ let toAdj = this.relationAdjacency.get(toLower);
166
+ if (!toAdj) {
167
+ toAdj = [];
168
+ this.relationAdjacency.set(toLower, toAdj);
169
+ }
170
+ toAdj.push(rel);
171
+ }
172
+ }
173
+ }
174
+ static getRuleDomainPriority(rule) {
175
+ const titleLower = rule.title.toLowerCase();
176
+ const dom = rule.domain;
177
+ if (titleLower.includes("biophys") && dom === "User_Preferences_And_Rules") {
178
+ return 0;
179
+ }
180
+ if (dom === "Knowledge_Base") {
181
+ return 1;
182
+ }
183
+ return 2;
184
+ }
185
+ static deduplicateRulesByTitle(rules) {
186
+ const byTitle = new Map;
187
+ for (const rule of rules) {
188
+ if (!rule.title)
189
+ continue;
190
+ const key = rule.title.toLowerCase();
191
+ const existing = byTitle.get(key);
192
+ if (!existing) {
193
+ byTitle.set(key, rule);
194
+ } else {
195
+ const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
196
+ const incomingPrio = CausalMemoryStore.getRuleDomainPriority(rule);
197
+ if (incomingPrio > existingPrio) {
198
+ byTitle.set(key, rule);
199
+ } else if (incomingPrio === existingPrio) {
200
+ const existingDate = existing.lastVerified || "";
201
+ const incomingDate = rule.lastVerified || "";
202
+ if (incomingDate >= existingDate) {
203
+ byTitle.set(key, rule);
204
+ }
205
+ }
206
+ }
207
+ }
208
+ return Array.from(byTitle.values());
209
+ }
210
+ loadDatabase() {
211
+ try {
212
+ if (existsSync(this.dbPath)) {
213
+ const stat = statSync(this.dbPath);
214
+ this.lastMtimeMs = stat.mtimeMs;
215
+ const raw = readFileSync(this.dbPath, "utf-8");
216
+ const parsed = JSON.parse(raw);
217
+ const rawRules = Array.isArray(parsed.rules) ? parsed.rules : [];
218
+ const rules = CausalMemoryStore.deduplicateRulesByTitle(rawRules);
219
+ const relations = Array.isArray(parsed.relations) ? parsed.relations : [];
220
+ return {
221
+ version: parsed.version || CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
222
+ updatedAt: parsed.updatedAt || new Date().toISOString(),
223
+ totalRules: rules.length,
224
+ totalRelations: relations.length,
225
+ rules,
226
+ relations
227
+ };
228
+ }
229
+ } catch {}
230
+ return {
231
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
232
+ updatedAt: new Date().toISOString(),
233
+ totalRules: 0,
234
+ totalRelations: 0,
235
+ rules: [],
236
+ relations: []
237
+ };
238
+ }
239
+ saveDatabase() {
240
+ try {
241
+ const dir = dirname(this.dbPath);
242
+ if (!existsSync(dir)) {
243
+ mkdirSync(dir, { recursive: true });
244
+ }
245
+ const serialized = JSON.stringify(this.database, null, 2);
246
+ writeFileSync(this.dbPath, serialized, "utf-8");
247
+ this.lastMtimeMs = statSync(this.dbPath).mtimeMs;
248
+ if (this.dbPath === CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH && existsSync(dirname(CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH))) {
249
+ writeFileSync(CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH, serialized, "utf-8");
250
+ }
251
+ } catch (error) {
252
+ throw new Error(`\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB514\uC2A4\uD06C \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
253
+ }
254
+ }
255
+ getAllRules() {
256
+ return this.database.rules;
257
+ }
258
+ getAllRelations() {
259
+ return this.database.relations;
260
+ }
261
+ getStatus() {
262
+ const domainCounts = {};
263
+ for (const [dom, list] of this.domainIndex.entries()) {
264
+ const originalDomainName = list[0]?.domain || dom;
265
+ domainCounts[originalDomainName] = list.length;
266
+ }
267
+ return {
268
+ version: this.database.version,
269
+ updatedAt: this.database.updatedAt,
270
+ totalRules: this.database.rules.length,
271
+ totalRelations: this.database.relations.length,
272
+ dbPath: this.dbPath,
273
+ domains: Object.keys(domainCounts),
274
+ domainCounts
275
+ };
276
+ }
277
+ getCompiledSearchRules() {
278
+ if (this.compiledSearchRules.length === 0 && this.database.rules.length > 0) {
279
+ for (const rule of this.database.rules) {
280
+ const domainLower = rule.domain.toLowerCase();
281
+ const searchableText = [
282
+ rule.title,
283
+ rule.domain,
284
+ ...rule.triggers.keywords || [],
285
+ ...rule.invariants,
286
+ ...rule.actions,
287
+ ...rule.context
288
+ ].join(" ").toLowerCase();
289
+ this.compiledSearchRules.push({ rule, domainLower, searchableText });
290
+ }
291
+ }
292
+ return this.compiledSearchRules;
293
+ }
294
+ queryResonance(options) {
295
+ const startTime = performance.now();
296
+ const targetDomain = options.domain ? options.domain.toLowerCase() : undefined;
297
+ const queryCommand = options.command ? options.command.toLowerCase() : undefined;
298
+ const propagate = options.propagateRelations ?? true;
299
+ const maxHops = options.maxHops ?? 1;
300
+ const matchedRulesMap = new Map;
301
+ const nonAlwaysMatchedTitles = new Set;
302
+ for (const rule of this.alwaysActiveRules) {
303
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
304
+ continue;
305
+ matchedRulesMap.set(rule.id, rule);
306
+ }
307
+ if (queryCommand) {
308
+ const cmdRules = this.commandIndex.get(queryCommand);
309
+ if (cmdRules) {
310
+ for (const rule of cmdRules) {
311
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
312
+ continue;
313
+ matchedRulesMap.set(rule.id, rule);
314
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
315
+ }
316
+ }
317
+ }
318
+ if (options.files && options.files.length > 0 && this.exactFileIndex.size > 0) {
319
+ const hasPrefixRules = this.prefixFileRules.length > 0;
320
+ const checkedDirs = new Set;
321
+ const registerHits = (hits) => {
322
+ if (!hits)
323
+ return;
324
+ for (let i = 0;i < hits.length; i++) {
325
+ const rule = hits[i];
326
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
327
+ continue;
328
+ matchedRulesMap.set(rule.id, rule);
329
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
330
+ }
331
+ };
332
+ let lastTwoSeg = "";
333
+ for (let i = 0;i < options.files.length; i++) {
334
+ const rawFile = options.files[i];
335
+ const nf = (rawFile.includes("\\") ? rawFile.replaceAll("\\", "/") : rawFile).toLowerCase();
336
+ registerHits(this.exactFileIndex.get(nf));
337
+ const firstSlash = nf.indexOf("/");
338
+ if (firstSlash > 0) {
339
+ const secondSlash = nf.indexOf("/", firstSlash + 1);
340
+ const twoSeg = secondSlash > 0 ? nf.slice(0, secondSlash) : nf.slice(0, firstSlash);
341
+ if (twoSeg !== lastTwoSeg) {
342
+ lastTwoSeg = twoSeg;
343
+ if (!checkedDirs.has(twoSeg)) {
344
+ checkedDirs.add(twoSeg);
345
+ registerHits(this.exactFileIndex.get(twoSeg));
346
+ const oneSeg = nf.slice(0, firstSlash);
347
+ const isDriveLetter = oneSeg.length === 2 && oneSeg.endsWith(":");
348
+ if (!isDriveLetter && oneSeg !== twoSeg && !checkedDirs.has(oneSeg)) {
349
+ checkedDirs.add(oneSeg);
350
+ registerHits(this.exactFileIndex.get(oneSeg));
351
+ }
352
+ }
353
+ }
354
+ const lastSlash = nf.lastIndexOf("/");
355
+ registerHits(this.exactFileIndex.get(nf.slice(lastSlash + 1)));
356
+ } else {
357
+ checkedDirs.add(nf);
358
+ }
359
+ }
360
+ if (hasPrefixRules && checkedDirs.size > 0) {
361
+ const prefixArr = Array.from(checkedDirs);
362
+ for (const entry of this.prefixFileRules) {
363
+ if (targetDomain && entry.domainLower !== targetDomain)
364
+ continue;
365
+ if (matchedRulesMap.has(entry.rule.id))
366
+ continue;
367
+ for (const pattern of entry.cleanPatterns) {
368
+ if (prefixArr.some((p) => p.includes(pattern) || p.length >= 4 && pattern.includes(p))) {
369
+ matchedRulesMap.set(entry.rule.id, entry.rule);
370
+ nonAlwaysMatchedTitles.add(entry.rule.title.toLowerCase());
371
+ break;
372
+ }
373
+ }
374
+ }
375
+ }
376
+ }
377
+ const queryKeywords = (options.keywords || []).flatMap((k) => k.toLowerCase().split(/\s+/)).filter((k) => k.length > 0);
378
+ if (queryKeywords.length > 0) {
379
+ const unindexedKeywords = [];
380
+ for (const qk of queryKeywords) {
381
+ const exactHits = this.exactKeywordIndex.get(qk);
382
+ if (exactHits && exactHits.length > 0) {
383
+ for (const rule of exactHits) {
384
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
385
+ continue;
386
+ matchedRulesMap.set(rule.id, rule);
387
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
388
+ }
389
+ } else {
390
+ unindexedKeywords.push(qk);
391
+ }
392
+ }
393
+ if (unindexedKeywords.length > 0) {
394
+ const searchPool = this.getCompiledSearchRules();
395
+ for (const entry of searchPool) {
396
+ if (targetDomain && entry.domainLower !== targetDomain)
397
+ continue;
398
+ if (matchedRulesMap.has(entry.rule.id))
399
+ continue;
400
+ for (const qk of unindexedKeywords) {
401
+ if (entry.searchableText.includes(qk)) {
402
+ matchedRulesMap.set(entry.rule.id, entry.rule);
403
+ nonAlwaysMatchedTitles.add(entry.rule.title.toLowerCase());
404
+ break;
405
+ }
406
+ }
407
+ }
408
+ }
409
+ }
410
+ const activeRelationsSet = new Set;
411
+ const visitedTitles = new Set(Array.from(matchedRulesMap.values()).map((r) => r.title.toLowerCase()));
412
+ let currentFrontier = new Set(nonAlwaysMatchedTitles.size > 0 ? nonAlwaysMatchedTitles : visitedTitles);
413
+ const effectiveHops = propagate ? Math.max(1, maxHops) : 1;
414
+ for (let hop = 0;hop < effectiveHops; hop++) {
415
+ const nextFrontier = new Set;
416
+ for (const titleLower of currentFrontier) {
417
+ const adjRelations = this.relationAdjacency.get(titleLower);
418
+ if (!adjRelations)
419
+ continue;
420
+ for (const rel of adjRelations) {
421
+ activeRelationsSet.add(rel);
422
+ if (propagate && nonAlwaysMatchedTitles.size > 0) {
423
+ const neighborLower = rel.from.toLowerCase() === titleLower ? rel.to.toLowerCase() : rel.from.toLowerCase();
424
+ if (!visitedTitles.has(neighborLower)) {
425
+ visitedTitles.add(neighborLower);
426
+ nextFrontier.add(neighborLower);
427
+ const neighborRule = this.titleToRuleMap.get(neighborLower);
428
+ if (neighborRule && (!targetDomain || neighborRule.domain.toLowerCase() === targetDomain)) {
429
+ matchedRulesMap.set(neighborRule.id, neighborRule);
430
+ }
431
+ }
432
+ }
433
+ }
434
+ }
435
+ if (nextFrontier.size === 0)
436
+ break;
437
+ currentFrontier = nextFrontier;
438
+ }
439
+ const matchedRules = Array.from(matchedRulesMap.values());
440
+ const activeRelations = Array.from(activeRelationsSet);
441
+ const duration = performance.now() - startTime;
442
+ const totalCount = this.database.rules.length;
443
+ const savings = totalCount > 0 ? ((totalCount - matchedRules.length) / totalCount * 100).toFixed(1) + "%" : "0.0%";
444
+ return {
445
+ queryDurationMs: Math.round(duration * 100) / 100,
446
+ matchedCount: matchedRules.length,
447
+ totalDatabaseRules: totalCount,
448
+ resonanceRules: matchedRules,
449
+ activeRelations,
450
+ tokenDietSavingsRatio: savings
451
+ };
452
+ }
453
+ assertRule(rule, persistToDisk = true) {
454
+ const targetTitle = (rule.title || rule.name || "").toLowerCase();
455
+ const existingIndex = this.database.rules.findIndex((r) => r.id === rule.id || targetTitle.length > 0 && (r.title || r.name || "").toLowerCase() === targetTitle);
456
+ const updatedRules = [...this.database.rules];
457
+ const today = new Date().toISOString().split("T")[0];
458
+ if (existingIndex >= 0) {
459
+ updatedRules[existingIndex] = {
460
+ ...rule,
461
+ id: rule.id || updatedRules[existingIndex].id,
462
+ lastVerified: today
463
+ };
464
+ } else {
465
+ updatedRules.push({
466
+ ...rule,
467
+ lastVerified: today
468
+ });
469
+ }
470
+ this.database = {
471
+ ...this.database,
472
+ updatedAt: new Date().toISOString(),
473
+ totalRules: updatedRules.length,
474
+ rules: updatedRules
475
+ };
476
+ this.rebuildIndices();
477
+ if (persistToDisk) {
478
+ this.saveDatabase();
479
+ }
480
+ }
481
+ readSmartGraph(files = []) {
482
+ const resonance = this.queryResonance({ files });
483
+ const status = this.getStatus();
484
+ const entities = resonance.resonanceRules.map((r) => ({
485
+ name: r.title,
486
+ entityType: r.entityType || r.domain,
487
+ domain: r.domain,
488
+ invariants: r.invariants,
489
+ actions: r.actions,
490
+ observations: [
491
+ ...r.invariants.map((i) => `[\uBD88\uBCC0\uADDC\uCE59] ${i}`),
492
+ ...r.actions.map((a) => `[\uC2E4\uD589\uC9C0\uCE68] ${a}`),
493
+ ...r.context
494
+ ]
495
+ }));
496
+ return {
497
+ causalMemoryEngine: `v${status.version} (Active Resonance Mode)`,
498
+ tokenDietSavings: resonance.tokenDietSavingsRatio,
499
+ queryDurationMs: resonance.queryDurationMs,
500
+ activeEntitiesCount: entities.length,
501
+ totalIndexedRules: status.totalRules,
502
+ totalIndexedRelations: status.totalRelations,
503
+ availableDomains: status.domainCounts,
504
+ hint: "\uBB34\uAD00\uD55C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC740 \uD1A0\uD070 \uC808\uAC10\uC744 \uC704\uD574 0ms \uC2A4\uD0B5\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uD2B9\uC815 \uB3C4\uBA54\uC778 \uC0C1\uC138 \uC870\uD68C\uB294 search_nodes \uB610\uB294 query_causal_memory\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
505
+ entities,
506
+ relations: resonance.activeRelations
507
+ };
508
+ }
509
+ searchNodes(query) {
510
+ const trimmed = query.trim();
511
+ if (!trimmed) {
512
+ return this.readSmartGraph();
513
+ }
514
+ const tokens = trimmed.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
515
+ const matchedRules = [];
516
+ const matchedTitles = new Set;
517
+ const searchPool = this.getCompiledSearchRules();
518
+ for (const entry of searchPool) {
519
+ const ruleIdLower = entry.rule.id.toLowerCase();
520
+ if (tokens.some((tok) => entry.searchableText.includes(tok) || ruleIdLower.includes(tok))) {
521
+ matchedRules.push(entry.rule);
522
+ matchedTitles.add(entry.rule.title.toLowerCase());
523
+ }
524
+ }
525
+ const relationsSet = new Set;
526
+ for (const titleLower of matchedTitles) {
527
+ const adj = this.relationAdjacency.get(titleLower);
528
+ if (adj) {
529
+ for (const r of adj)
530
+ relationsSet.add(r);
531
+ }
532
+ }
533
+ return {
534
+ entities: matchedRules.map((r) => ({
535
+ name: r.title,
536
+ entityType: r.entityType || r.domain,
537
+ observations: [...r.invariants, ...r.actions, ...r.context]
538
+ })),
539
+ relations: Array.from(relationsSet)
540
+ };
541
+ }
542
+ openNodes(names) {
543
+ const matchedRules = [];
544
+ const matchedTitles = new Set;
545
+ for (const name of names) {
546
+ const lower = name.toLowerCase();
547
+ const rule = this.titleToRuleMap.get(lower) || this.idToRuleMap.get(lower);
548
+ if (rule && !matchedTitles.has(rule.title.toLowerCase())) {
549
+ matchedRules.push(rule);
550
+ matchedTitles.add(rule.title.toLowerCase());
551
+ }
552
+ }
553
+ const relationsSet = new Set;
554
+ for (const titleLower of matchedTitles) {
555
+ const adj = this.relationAdjacency.get(titleLower);
556
+ if (adj) {
557
+ for (const r of adj)
558
+ relationsSet.add(r);
559
+ }
560
+ }
561
+ return {
562
+ entities: matchedRules.map((r) => ({
563
+ name: r.title,
564
+ entityType: r.entityType || r.domain,
565
+ observations: [...r.invariants, ...r.actions, ...r.context]
566
+ })),
567
+ relations: Array.from(relationsSet)
568
+ };
569
+ }
570
+ createEntities(entities, persistToDisk = true) {
571
+ const created = [];
572
+ const updatedRules = [...this.database.rules];
573
+ const today = new Date().toISOString().split("T")[0];
574
+ for (const ent of entities) {
575
+ if (this.titleToRuleMap.has(ent.name.toLowerCase()))
576
+ continue;
577
+ const inferred = CausalMemoryStore.inferRuleFromEntity(ent, today);
578
+ updatedRules.push(inferred);
579
+ created.push(ent);
580
+ }
581
+ if (created.length > 0) {
582
+ this.database = {
583
+ ...this.database,
584
+ updatedAt: new Date().toISOString(),
585
+ totalRules: updatedRules.length,
586
+ rules: updatedRules
587
+ };
588
+ this.rebuildIndices();
589
+ if (persistToDisk) {
590
+ this.saveDatabase();
591
+ }
592
+ }
593
+ return created;
594
+ }
595
+ addObservations(observations, persistToDisk = true) {
596
+ const updatedRules = [...this.database.rules];
597
+ const results = [];
598
+ let changed = false;
599
+ for (const item of observations) {
600
+ const idx = updatedRules.findIndex((r) => r.title.toLowerCase() === item.entityName.toLowerCase());
601
+ if (idx < 0)
602
+ continue;
603
+ const rule = updatedRules[idx];
604
+ const existingObs = new Set([...rule.invariants, ...rule.actions, ...rule.context]);
605
+ const added = [];
606
+ const newInvariants = [...rule.invariants];
607
+ const newActions = [...rule.actions];
608
+ const newContext = [...rule.context];
609
+ for (const content of item.contents) {
610
+ if (existingObs.has(content))
611
+ continue;
612
+ added.push(content);
613
+ existingObs.add(content);
614
+ changed = true;
615
+ if (content.includes("\uAE08\uC9C0") || content.includes("\uC808\uB300") || content.includes("\uD544\uC218") || content.includes("\uC8FC\uC758")) {
616
+ newInvariants.push(content);
617
+ } else if (/^\d+\.|\uB2E8\uACC4|Step/i.test(content)) {
618
+ newActions.push(content);
619
+ } else {
620
+ newContext.push(content);
621
+ }
622
+ }
623
+ updatedRules[idx] = {
624
+ ...rule,
625
+ invariants: newInvariants,
626
+ actions: newActions,
627
+ context: newContext,
628
+ lastVerified: new Date().toISOString().split("T")[0]
629
+ };
630
+ results.push({ entityName: item.entityName, addedObservations: added });
631
+ }
632
+ if (changed) {
633
+ this.database = {
634
+ ...this.database,
635
+ updatedAt: new Date().toISOString(),
636
+ rules: updatedRules
637
+ };
638
+ this.rebuildIndices();
639
+ if (persistToDisk) {
640
+ this.saveDatabase();
641
+ }
642
+ }
643
+ return results;
644
+ }
645
+ createRelations(relations, persistToDisk = true) {
646
+ const updatedRelations = [...this.database.relations];
647
+ const created = [];
648
+ for (const rel of relations) {
649
+ const exists = updatedRelations.some((r) => r.from === rel.from && r.to === rel.to && r.relationType === rel.relationType);
650
+ if (!exists) {
651
+ updatedRelations.push(rel);
652
+ created.push(rel);
653
+ }
654
+ }
655
+ if (created.length > 0) {
656
+ this.database = {
657
+ ...this.database,
658
+ updatedAt: new Date().toISOString(),
659
+ totalRelations: updatedRelations.length,
660
+ relations: updatedRelations
661
+ };
662
+ this.rebuildIndices();
663
+ if (persistToDisk) {
664
+ this.saveDatabase();
665
+ }
666
+ }
667
+ return created;
668
+ }
669
+ deleteRule(ruleId, persistToDisk = true) {
670
+ const targetId = ruleId.toLowerCase();
671
+ const removedTitles = new Set;
672
+ const filteredRules = this.database.rules.filter((r) => {
673
+ if (r.id.toLowerCase() === targetId) {
674
+ removedTitles.add(r.title.toLowerCase());
675
+ return false;
676
+ }
677
+ return true;
678
+ });
679
+ if (removedTitles.size === 0) {
680
+ return false;
681
+ }
682
+ const filteredRelations = this.database.relations.filter((rel) => !removedTitles.has(rel.from.toLowerCase()) && !removedTitles.has(rel.to.toLowerCase()) && rel.from.toLowerCase() !== targetId && rel.to.toLowerCase() !== targetId);
683
+ this.database = {
684
+ ...this.database,
685
+ updatedAt: new Date().toISOString(),
686
+ totalRules: filteredRules.length,
687
+ totalRelations: filteredRelations.length,
688
+ rules: filteredRules,
689
+ relations: filteredRelations
690
+ };
691
+ this.rebuildIndices();
692
+ if (persistToDisk) {
693
+ this.saveDatabase();
694
+ }
695
+ return true;
696
+ }
697
+ deleteEntities(entityNames, persistToDisk = true) {
698
+ const nameSet = new Set(entityNames.map((n) => n.toLowerCase()));
699
+ const filteredRules = this.database.rules.filter((r) => !nameSet.has(r.title.toLowerCase()) && !nameSet.has(r.id.toLowerCase()));
700
+ const filteredRelations = this.database.relations.filter((rel) => !nameSet.has(rel.from.toLowerCase()) && !nameSet.has(rel.to.toLowerCase()));
701
+ this.database = {
702
+ ...this.database,
703
+ updatedAt: new Date().toISOString(),
704
+ totalRules: filteredRules.length,
705
+ totalRelations: filteredRelations.length,
706
+ rules: filteredRules,
707
+ relations: filteredRelations
708
+ };
709
+ this.rebuildIndices();
710
+ if (persistToDisk) {
711
+ this.saveDatabase();
712
+ }
713
+ }
714
+ deleteObservations(deletions, persistToDisk = true) {
715
+ const updatedRules = [...this.database.rules];
716
+ for (const del of deletions) {
717
+ const idx = updatedRules.findIndex((r) => r.title.toLowerCase() === del.entityName.toLowerCase());
718
+ if (idx < 0)
719
+ continue;
720
+ const toDelete = new Set(del.observations);
721
+ const rule = updatedRules[idx];
722
+ updatedRules[idx] = {
723
+ ...rule,
724
+ invariants: rule.invariants.filter((x) => !toDelete.has(x)),
725
+ actions: rule.actions.filter((x) => !toDelete.has(x)),
726
+ context: rule.context.filter((x) => !toDelete.has(x))
727
+ };
728
+ }
729
+ this.database = {
730
+ ...this.database,
731
+ updatedAt: new Date().toISOString(),
732
+ rules: updatedRules
733
+ };
734
+ this.rebuildIndices();
735
+ if (persistToDisk) {
736
+ this.saveDatabase();
737
+ }
738
+ }
739
+ deleteRelations(relations, persistToDisk = true) {
740
+ const filtered = this.database.relations.filter((existing) => !relations.some((del) => del.from === existing.from && del.to === existing.to && del.relationType === existing.relationType));
741
+ this.database = {
742
+ ...this.database,
743
+ updatedAt: new Date().toISOString(),
744
+ totalRelations: filtered.length,
745
+ relations: filtered
746
+ };
747
+ this.rebuildIndices();
748
+ if (persistToDisk) {
749
+ this.saveDatabase();
750
+ }
751
+ }
752
+ static inferRuleFromEntity(ent, today) {
753
+ const nameLower = ent.name.toLowerCase();
754
+ const typeLower = ent.entityType.toLowerCase();
755
+ const obsJoined = ent.observations.join(" ").toLowerCase();
756
+ let domain = "Knowledge_Base";
757
+ const triggers = {};
758
+ const invariants = [];
759
+ const actions = [];
760
+ const context = [];
761
+ const isTrueUserPreference = !nameLower.includes("biophys") && (typeLower === "preference" || typeLower === "user_constraint" || typeLower === "forbidden_rules" || nameLower === "userpreferences" || nameLower === "user_web_navigation_preference" || nameLower === "python_strictly_forbidden" || nameLower === "forbidden_topics_until_mentioned");
762
+ if (isTrueUserPreference) {
763
+ domain = "User_Preferences_And_Rules";
764
+ triggers.alwaysActive = true;
765
+ triggers.keywords = ["\uD658\uACBD\uC124\uC815", "\uADDC\uCE59", "\uD130\uBBF8\uB110", "\uCD9C\uB825", "\uBCF4\uC548", "\uC120\uD638\uB3C4"];
766
+ } else if (nameLower.includes("poe2") || nameLower.includes("noble-babbage") || nameLower.includes("release_workflow") || obsJoined.includes("poe2filter-releases") || obsJoined.includes("noble-babbage")) {
767
+ domain = "Project_POE2_AutoFilter";
768
+ triggers.files = [
769
+ "noble-babbage",
770
+ "poe2filter-releases",
771
+ "updater.json",
772
+ "post_download_button.js"
773
+ ];
774
+ triggers.keywords = ["poe2", "autofilter", "\uD544\uD130", "\uC624\uD1A0\uD544\uD130", "updater", "discord", "\uB9B4\uB9AC\uC988", "\uBC30\uD3EC"];
775
+ triggers.commands = ["release", "updater", "build", "sign"];
776
+ } else if (nameLower.includes("vesper") || nameLower.includes("dsp") || nameLower.includes("neurospatial audio") || typeLower.includes("audio_architecture")) {
777
+ domain = "Project_Vesper_DSP";
778
+ triggers.files = ["vesper", "crates/vesper-", "vesper-apo"];
779
+ triggers.keywords = ["vesper", "dsp", "\uC624\uB514\uC624", "audio", "mmcss", "virtuallock", "apo", "eq", "\uC0AC\uC6B4\uB4DC"];
780
+ triggers.commands = ["check", "build", "test"];
781
+ } else if (nameLower.includes("synapse") && !nameLower.includes("biophys")) {
782
+ domain = "Project_Synapse_Kit";
783
+ triggers.files = ["synapse", "synapse.config.ts"];
784
+ triggers.keywords = ["synapse", "synapse-kit", "\uC2DC\uB0C5\uC2A4", "\uC778\uACFC\uC728", "causal", "\uCEF4\uD30C\uC77C\uB7EC", "\uCCB4\uCEE4"];
785
+ triggers.commands = ["check", "build", "package", "sign", "init", "memory"];
786
+ } else if (nameLower.includes("hardware") || nameLower.includes("pcb") || nameLower.includes("jlcpcb") || nameLower.includes("m2_2230") || nameLower.includes("pocket_orange") || typeLower.includes("hardware")) {
787
+ domain = "Hardware_And_PCB";
788
+ triggers.files = ["hardware", "pcb", "kicad"];
789
+ triggers.keywords = ["pcb", "hardware", "\uD558\uB4DC\uC6E8\uC5B4", "\uD68C\uB85C", "jlcpcb", "kicad", "m2"];
790
+ } else if (nameLower.includes("biophys") || nameLower.includes("connectome") || nameLower.includes("drosophila") || nameLower.includes("cortex") || typeLower.includes("whitepaper")) {
791
+ domain = "BioPhys_Research_And_Architecture";
792
+ triggers.files = ["biophys", "whitepaper"];
793
+ triggers.keywords = ["biophys", "connectome", "\uC2DC\uB0C5\uC2A4\uC2E0\uACBD\uB9DD", "\uD000\uD0C0\uC774\uC81C\uC774\uC158", "\uBC31\uC11C", "\uC544\uD0A4\uD14D\uCC98"];
794
+ } else {
795
+ domain = "Knowledge_Base";
796
+ triggers.keywords = [ent.name.toLowerCase().replace(/_/g, " ")];
797
+ }
798
+ for (const obs of ent.observations) {
799
+ if (obs.includes("\uAE08\uC9C0") || obs.includes("\uC808\uB300") || obs.includes("\uD544\uC218") || obs.includes("\uC8FC\uC758") || obs.includes("VirtualLock") || obs.includes("MMCSS")) {
800
+ invariants.push(obs);
801
+ } else if (/^\d+\.|\uB2E8\uACC4|Step/i.test(obs)) {
802
+ actions.push(obs);
803
+ } else {
804
+ context.push(obs);
805
+ }
806
+ }
807
+ const baseSlug = ent.name.trim().toLowerCase().replace(/[^\w\uAC00-\uD7A30-9-]+/g, "-").replace(/^-+|-+$/g, "") || "item";
808
+ return {
809
+ id: `rule-${domain.toLowerCase()}-${baseSlug}`,
810
+ domain,
811
+ title: ent.name,
812
+ entityType: ent.entityType,
813
+ triggers,
814
+ invariants,
815
+ actions,
816
+ context,
817
+ lastVerified: today
818
+ };
819
+ }
820
+ audit() {
821
+ const issues = [];
822
+ const seenIds = new Set;
823
+ const seenTitles = new Set;
824
+ let alwaysActiveCount = 0;
825
+ for (const rule of this.database.rules) {
826
+ if (rule.triggers.alwaysActive) {
827
+ alwaysActiveCount++;
828
+ }
829
+ if (!rule.id || rule.id.trim().length === 0) {
830
+ issues.push(`ID \uB204\uB77D \uADDC\uCE59 \uBC1C\uACAC: ${rule.title}`);
831
+ } else if (seenIds.has(rule.id)) {
832
+ issues.push(`\uC911\uBCF5 ID \uBC1C\uACAC: ${rule.id}`);
833
+ } else {
834
+ seenIds.add(rule.id);
835
+ }
836
+ const titleLower = (rule.title || "").toLowerCase();
837
+ if (titleLower) {
838
+ if (seenTitles.has(titleLower)) {
839
+ issues.push(`\uC911\uBCF5 \uC81C\uBAA9(Title) \uBC1C\uACAC: ${rule.title}`);
840
+ } else {
841
+ seenTitles.add(titleLower);
842
+ }
843
+ }
844
+ if (!rule.domain) {
845
+ issues.push(`\uB3C4\uBA54\uC778 \uB204\uB77D \uADDC\uCE59: ${rule.id}`);
846
+ }
847
+ }
848
+ if (alwaysActiveCount > 10) {
849
+ issues.push(`\uC0C1\uC2DC \uD65C\uC131(alwaysActive) \uADDC\uCE59 \uACFC\uB2E4 (${alwaysActiveCount}\uAC1C): \uD1A0\uD070 \uC808\uAC10 \uD6A8\uC728\uC774 \uC800\uD558\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`);
850
+ }
851
+ return {
852
+ ok: issues.length === 0,
853
+ totalRules: this.database.rules.length,
854
+ totalRelations: this.database.relations.length,
855
+ alwaysActiveCount,
856
+ issues
857
+ };
858
+ }
859
+ getRawDatabase() {
860
+ this.reloadIfNeeded();
861
+ return this.database;
862
+ }
863
+ mergeDatabase(incomingRules, incomingRelations = []) {
864
+ this.reloadIfNeeded();
865
+ let addedRules = 0;
866
+ let updatedRules = 0;
867
+ let addedRelations = 0;
868
+ const byTitle = new Map;
869
+ for (const r of this.database.rules) {
870
+ byTitle.set(r.title.toLowerCase(), r);
871
+ }
872
+ for (const incoming of incomingRules) {
873
+ if (!incoming.id || !incoming.title)
874
+ continue;
875
+ const titleKey = incoming.title.toLowerCase();
876
+ const existing = byTitle.get(titleKey);
877
+ if (!existing) {
878
+ byTitle.set(titleKey, incoming);
879
+ addedRules++;
880
+ } else {
881
+ const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
882
+ const incomingPrio = CausalMemoryStore.getRuleDomainPriority(incoming);
883
+ const existingDate = existing.lastVerified || "";
884
+ const incomingDate = incoming.lastVerified || "";
885
+ if (incomingPrio > existingPrio) {
886
+ byTitle.set(titleKey, incoming);
887
+ updatedRules++;
888
+ } else if (incomingPrio === existingPrio && incoming.id !== existing.id && incomingDate > existingDate) {
889
+ byTitle.set(titleKey, incoming);
890
+ updatedRules++;
891
+ } else if (incomingPrio === existingPrio && incoming.id === existing.id && incomingDate > existingDate) {
892
+ byTitle.set(titleKey, incoming);
893
+ updatedRules++;
894
+ }
895
+ }
896
+ }
897
+ const relationKey = (rel) => `${rel.from.toLowerCase()}::${rel.relationType.toLowerCase()}::${rel.to.toLowerCase()}`;
898
+ const relMap = new Map;
899
+ for (const rel of this.database.relations) {
900
+ relMap.set(relationKey(rel), rel);
901
+ }
902
+ for (const incomingRel of incomingRelations) {
903
+ const key = relationKey(incomingRel);
904
+ if (!relMap.has(key)) {
905
+ relMap.set(key, incomingRel);
906
+ addedRelations++;
907
+ }
908
+ }
909
+ const mergedRules = Array.from(byTitle.values());
910
+ const mergedRelations = Array.from(relMap.values());
911
+ this.database = {
912
+ ...this.database,
913
+ updatedAt: new Date().toISOString(),
914
+ totalRules: mergedRules.length,
915
+ totalRelations: mergedRelations.length,
916
+ rules: mergedRules,
917
+ relations: mergedRelations
918
+ };
919
+ this.rebuildIndices();
920
+ this.saveDatabase();
921
+ return {
922
+ addedRules,
923
+ updatedRules,
924
+ addedRelations,
925
+ totalRules: mergedRules.length,
926
+ totalRelations: mergedRelations.length
927
+ };
928
+ }
929
+ }
930
+ // memory/causal-query.ts
931
+ var {spawn } = globalThis.Bun;
932
+ async function detectModifiedFiles(cwd = process.cwd()) {
933
+ try {
934
+ const gitProc = spawn(["git", "status", "--porcelain", "-uall"], {
935
+ cwd,
936
+ stdout: "pipe",
937
+ stderr: "pipe"
938
+ });
939
+ const output = await new Response(gitProc.stdout).text();
940
+ await gitProc.exited;
941
+ return output.split(`
942
+ `).map((line) => line.slice(3).trim().replaceAll("\\", "/")).filter((line) => line.length > 0 && !line.startsWith(".synapse/") && !line.includes("/.synapse/"));
943
+ } catch {
944
+ return [];
945
+ }
946
+ }
947
+ async function runCausalQuery(rawOptions, storeCustomPath) {
948
+ const store = new CausalMemoryStore(storeCustomPath);
949
+ let targetFiles = rawOptions.files ? [...rawOptions.files] : [];
950
+ if (rawOptions.autoGit || targetFiles.length === 0) {
951
+ const gitFiles = await detectModifiedFiles(rawOptions.cwd || process.cwd());
952
+ if (gitFiles.length > 0) {
953
+ targetFiles = Array.from(new Set([...targetFiles, ...gitFiles]));
954
+ }
955
+ }
956
+ const queryOptions = {
957
+ files: targetFiles,
958
+ keywords: rawOptions.keywords,
959
+ command: rawOptions.command,
960
+ domain: rawOptions.domain,
961
+ propagateRelations: rawOptions.propagateRelations
962
+ };
963
+ return store.queryResonance(queryOptions);
964
+ }
965
+ function formatResonanceForAgent(result, compact = false) {
966
+ if (result.matchedCount === 0) {
967
+ return `[Causal-Memory] \uACF5\uBA85 \uADDC\uCE59 0\uAC74 (\uD1A0\uD070 \uC808\uAC10\uB960: ${result.tokenDietSavingsRatio}, \uC18C\uC694: ${result.queryDurationMs}ms)`;
968
+ }
969
+ if (compact) {
970
+ return JSON.stringify({
971
+ ok: true,
972
+ durationMs: result.queryDurationMs,
973
+ matchedCount: result.matchedCount,
974
+ totalDatabaseRules: result.totalDatabaseRules,
975
+ savings: result.tokenDietSavingsRatio,
976
+ rules: result.resonanceRules.map((r) => ({
977
+ id: r.id,
978
+ domain: r.domain,
979
+ title: r.title,
980
+ invariants: r.invariants,
981
+ actions: r.actions
982
+ })),
983
+ relations: result.activeRelations
984
+ });
985
+ }
986
+ const lines = [
987
+ `### \uD83E\uDDE0 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uACF5\uBA85 \uC8FC\uC785 (${result.matchedCount}/${result.totalDatabaseRules}\uAC1C \uADDC\uCE59 \uD65C\uC131\uD654, \uC808\uAC10\uB960: ${result.tokenDietSavingsRatio}, ${result.queryDurationMs}ms)`
988
+ ];
989
+ for (const rule of result.resonanceRules) {
990
+ lines.push(`
991
+ **[${rule.domain}] ${rule.title}**`);
992
+ if (rule.invariants.length > 0) {
993
+ lines.push(" - **\uBD88\uBCC0 \uADDC\uCE59(Invariants)**:");
994
+ for (const inv of rule.invariants) {
995
+ lines.push(` \u2022 ${inv}`);
996
+ }
997
+ }
998
+ if (rule.actions.length > 0) {
999
+ lines.push(" - **\uC2E4\uD589 \uC808\uCC28(Actions)**:");
1000
+ for (const act of rule.actions) {
1001
+ lines.push(` \u2022 ${act}`);
1002
+ }
1003
+ }
1004
+ if (rule.context.length > 0) {
1005
+ lines.push(" - **\uD575\uC2EC \uB9E5\uB77D(Context)**:");
1006
+ for (const ctx of rule.context.slice(0, CAUSAL_MEMORY_CONFIG.MAX_CONTEXT_PREVIEW_LINES)) {
1007
+ lines.push(` \u2022 ${ctx}`);
1008
+ }
1009
+ }
1010
+ }
1011
+ if (result.activeRelations.length > 0) {
1012
+ lines.push(`
1013
+ ### \uD83D\uDD17 \uD65C\uC131 \uC778\uACFC \uAC04\uC120 (Active Causal Relations)`);
1014
+ for (const rel of result.activeRelations) {
1015
+ lines.push(` \u2022 \`${rel.from}\` \u2500\u2500[${rel.relationType}]\u2500\u2500\u25B6 \`${rel.to}\``);
1016
+ }
1017
+ }
1018
+ return lines.join(`
1019
+ `);
1020
+ }
1021
+ // memory/causal-server.ts
1022
+ import { createInterface } from "readline";
1023
+
1024
+ // memory/causal-context.ts
1025
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, renameSync, statSync as statSync2 } from "fs";
1026
+ import { dirname as dirname2 } from "path";
1027
+ var {spawn: spawn2 } = globalThis.Bun;
1028
+ async function detectGitHeadInfo(cwd = process.cwd()) {
1029
+ try {
1030
+ const branchProc = spawn2(["git", "rev-parse", "--abbrev-ref", "HEAD"], {
1031
+ cwd,
1032
+ stdout: "pipe",
1033
+ stderr: "pipe"
1034
+ });
1035
+ const branchOut = (await new Response(branchProc.stdout).text()).trim();
1036
+ const branchExit = await branchProc.exited;
1037
+ const commitProc = spawn2(["git", "rev-parse", "--short", "HEAD"], {
1038
+ cwd,
1039
+ stdout: "pipe",
1040
+ stderr: "pipe"
1041
+ });
1042
+ const commitOut = (await new Response(commitProc.stdout).text()).trim();
1043
+ const commitExit = await commitProc.exited;
1044
+ return {
1045
+ branch: branchExit === 0 && branchOut.length > 0 ? branchOut : undefined,
1046
+ commit: commitExit === 0 && commitOut.length > 0 ? commitOut : undefined
1047
+ };
1048
+ } catch {
1049
+ return {};
1050
+ }
1051
+ }
1052
+
1053
+ class ContextCheckpointStore {
1054
+ dbPath;
1055
+ memoryDbPath;
1056
+ database;
1057
+ lastMtimeMs = 0;
1058
+ constructor(customPath, memoryDbPath) {
1059
+ this.dbPath = customPath || CAUSAL_MEMORY_CONFIG.CONTEXT_DB_PATH;
1060
+ this.memoryDbPath = memoryDbPath;
1061
+ this.database = this.loadDatabase();
1062
+ }
1063
+ loadDatabase() {
1064
+ try {
1065
+ if (existsSync2(this.dbPath)) {
1066
+ const stat = statSync2(this.dbPath);
1067
+ this.lastMtimeMs = stat.mtimeMs;
1068
+ const raw = readFileSync2(this.dbPath, "utf-8");
1069
+ const parsed = JSON.parse(raw);
1070
+ if (parsed && Array.isArray(parsed.checkpoints)) {
1071
+ return parsed;
1072
+ }
1073
+ }
1074
+ } catch {}
1075
+ return {
1076
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1077
+ updatedAt: new Date().toISOString(),
1078
+ latestCheckpointId: null,
1079
+ totalCheckpoints: 0,
1080
+ checkpoints: []
1081
+ };
1082
+ }
1083
+ reloadIfNeeded() {
1084
+ try {
1085
+ if (existsSync2(this.dbPath)) {
1086
+ const stat = statSync2(this.dbPath);
1087
+ if (stat.mtimeMs > this.lastMtimeMs) {
1088
+ this.database = this.loadDatabase();
1089
+ }
1090
+ }
1091
+ } catch {}
1092
+ }
1093
+ saveDatabase() {
1094
+ try {
1095
+ const dir = dirname2(this.dbPath);
1096
+ if (!existsSync2(dir)) {
1097
+ mkdirSync2(dir, { recursive: true });
1098
+ }
1099
+ const serialized = JSON.stringify(this.database, null, 2);
1100
+ const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
1101
+ writeFileSync2(tempPath, serialized, "utf-8");
1102
+ renameSync(tempPath, this.dbPath);
1103
+ try {
1104
+ const stat = statSync2(this.dbPath);
1105
+ this.lastMtimeMs = stat.mtimeMs;
1106
+ } catch {}
1107
+ if (this.dbPath === CAUSAL_MEMORY_CONFIG.CONTEXT_DB_PATH) {
1108
+ try {
1109
+ const localDir = dirname2(CAUSAL_MEMORY_CONFIG.LOCAL_CONTEXT_DB_PATH);
1110
+ if (existsSync2(localDir)) {
1111
+ writeFileSync2(CAUSAL_MEMORY_CONFIG.LOCAL_CONTEXT_DB_PATH, serialized, "utf-8");
1112
+ }
1113
+ } catch {}
1114
+ }
1115
+ } catch (error) {
1116
+ throw new Error(`\uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
1117
+ }
1118
+ }
1119
+ static generateCheckpointId() {
1120
+ const now = new Date;
1121
+ const pad = (n) => String(n).padStart(2, "0");
1122
+ const datePart = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
1123
+ const timePart = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
1124
+ const randPart = Math.random().toString(36).slice(2, 6);
1125
+ return `ctx_${datePart}_${timePart}_${randPart}`;
1126
+ }
1127
+ async saveCheckpoint(input) {
1128
+ this.reloadIfNeeded();
1129
+ if (!input.title || input.title.trim().length === 0) {
1130
+ throw new Error("\uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC81C\uBAA9(title)\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
1131
+ }
1132
+ const cwd = input.cwd || process.cwd();
1133
+ const sessionId = (input.sessionId || "default-session").trim();
1134
+ const autoGit = input.autoGit !== false;
1135
+ const autoResonance = input.autoResonance !== false;
1136
+ let modifiedFiles = input.modifiedFiles ? [...input.modifiedFiles] : [];
1137
+ let gitBranch;
1138
+ let gitCommit;
1139
+ if (autoGit) {
1140
+ const [detectedFiles, gitHead] = await Promise.all([
1141
+ modifiedFiles.length === 0 ? detectModifiedFiles(cwd) : Promise.resolve(modifiedFiles),
1142
+ detectGitHeadInfo(cwd)
1143
+ ]);
1144
+ modifiedFiles = detectedFiles;
1145
+ gitBranch = gitHead.branch;
1146
+ gitCommit = gitHead.commit;
1147
+ }
1148
+ const activeFiles = input.activeFiles ? Array.from(new Set(input.activeFiles)) : modifiedFiles.slice(0, 15);
1149
+ const invariantsSet = new Set(input.activeInvariants || []);
1150
+ if (autoResonance) {
1151
+ try {
1152
+ const memoryStore = new CausalMemoryStore(this.memoryDbPath);
1153
+ const resonance = memoryStore.queryResonance({
1154
+ files: activeFiles.length > 0 ? activeFiles : modifiedFiles,
1155
+ keywords: input.keywords,
1156
+ domain: input.domain,
1157
+ propagateRelations: false
1158
+ });
1159
+ for (const rule of resonance.resonanceRules) {
1160
+ for (const inv of rule.invariants) {
1161
+ invariantsSet.add(inv);
1162
+ }
1163
+ }
1164
+ } catch {}
1165
+ }
1166
+ const checkpointId = input.id?.trim() || ContextCheckpointStore.generateCheckpointId();
1167
+ const nowIso = new Date().toISOString();
1168
+ const checkpoint = {
1169
+ id: checkpointId,
1170
+ sessionId,
1171
+ title: input.title.trim(),
1172
+ createdAt: nowIso,
1173
+ cwd: cwd.replaceAll("\\", "/"),
1174
+ gitBranch,
1175
+ gitCommit,
1176
+ modifiedFiles,
1177
+ activeFiles,
1178
+ goal: (input.goal || input.title).trim(),
1179
+ completedSteps: input.completedSteps ? [...input.completedSteps] : [],
1180
+ pendingTasks: input.pendingTasks ? [...input.pendingTasks] : [],
1181
+ keyDecisions: input.keyDecisions ? [...input.keyDecisions] : [],
1182
+ activeInvariants: Array.from(invariantsSet),
1183
+ artifacts: input.artifacts ? [...input.artifacts] : [],
1184
+ customMeta: input.customMeta
1185
+ };
1186
+ const filtered = this.database.checkpoints.filter((c) => c.id !== checkpoint.id);
1187
+ const maxRetained = Math.max(1, CAUSAL_MEMORY_CONFIG.MAX_CHECKPOINTS_RETAINED);
1188
+ const updatedList = [checkpoint, ...filtered].slice(0, maxRetained);
1189
+ this.database = {
1190
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1191
+ updatedAt: nowIso,
1192
+ latestCheckpointId: checkpoint.id,
1193
+ totalCheckpoints: updatedList.length,
1194
+ checkpoints: updatedList
1195
+ };
1196
+ this.saveDatabase();
1197
+ if (autoResonance) {
1198
+ try {
1199
+ const memoryStore = new CausalMemoryStore(this.memoryDbPath);
1200
+ memoryStore.assertRule({
1201
+ id: "ctx-active-session-snapshot",
1202
+ domain: "User_Preferences_And_Rules",
1203
+ title: "Active_Session_Context_Snapshot",
1204
+ entityType: "ContextCheckpoint",
1205
+ triggers: {
1206
+ alwaysActive: true,
1207
+ keywords: ["context", "checkpoint", "resume", "\uCEE8\uD14D\uC2A4\uD2B8", "\uC774\uC5B4\uC11C", checkpoint.sessionId]
1208
+ },
1209
+ invariants: [
1210
+ `[\uCD5C\uADFC \uC791\uC5C5 \uBAA9\uD45C] ${checkpoint.goal}`,
1211
+ ...checkpoint.pendingTasks.length > 0 ? [`[\uB2E4\uC74C \uCD5C\uC6B0\uC120 \uC9C4\uD589 \uACFC\uC81C] ${checkpoint.pendingTasks[0]}`] : []
1212
+ ],
1213
+ actions: checkpoint.pendingTasks.slice(0, 6),
1214
+ context: [
1215
+ `\uCCB4\uD06C\uD3EC\uC778\uD2B8 ID: ${checkpoint.id} (\uC138\uC158: ${checkpoint.sessionId}, \uC2DC\uAC01: ${checkpoint.createdAt})`,
1216
+ `\uC81C\uBAA9: ${checkpoint.title}`,
1217
+ ...checkpoint.completedSteps.length > 0 ? [`\uC644\uB8CC \uB0B4\uC5ED: ${checkpoint.completedSteps.join(" | ")}`] : [],
1218
+ ...checkpoint.keyDecisions.length > 0 ? [`\uD575\uC2EC \uACB0\uC815: ${checkpoint.keyDecisions.join(" | ")}`] : [],
1219
+ ...checkpoint.artifacts.length > 0 ? [`\uC124\uACC4 \uC544\uD2F0\uD329\uD2B8: ${checkpoint.artifacts.join(", ")}`] : []
1220
+ ]
1221
+ });
1222
+ } catch {}
1223
+ }
1224
+ return checkpoint;
1225
+ }
1226
+ getLatestCheckpoint(sessionId) {
1227
+ this.reloadIfNeeded();
1228
+ if (this.database.checkpoints.length === 0) {
1229
+ return null;
1230
+ }
1231
+ if (sessionId && sessionId.trim().length > 0) {
1232
+ const targetSession = sessionId.trim().toLowerCase();
1233
+ return this.database.checkpoints.find((c) => c.sessionId.toLowerCase() === targetSession) || null;
1234
+ }
1235
+ return this.database.checkpoints[0] || null;
1236
+ }
1237
+ getCheckpoint(idOrTag, sessionId) {
1238
+ this.reloadIfNeeded();
1239
+ if (!idOrTag || idOrTag === "latest") {
1240
+ return this.getLatestCheckpoint(sessionId);
1241
+ }
1242
+ const cleanId = idOrTag.trim();
1243
+ const exact = this.database.checkpoints.find((c) => c.id === cleanId);
1244
+ if (exact)
1245
+ return exact;
1246
+ const cleanLower = cleanId.toLowerCase();
1247
+ return this.database.checkpoints.find((c) => c.id.toLowerCase().includes(cleanLower) || c.title.toLowerCase().includes(cleanLower) || c.sessionId.toLowerCase() === cleanLower) || null;
1248
+ }
1249
+ listCheckpoints(options) {
1250
+ this.reloadIfNeeded();
1251
+ let list = [...this.database.checkpoints];
1252
+ if (options?.sessionId && options.sessionId.trim().length > 0) {
1253
+ const target = options.sessionId.trim().toLowerCase();
1254
+ list = list.filter((c) => c.sessionId.toLowerCase() === target);
1255
+ }
1256
+ const limit = options?.limit && options.limit > 0 ? options.limit : list.length;
1257
+ return list.slice(0, limit);
1258
+ }
1259
+ deleteCheckpoint(id) {
1260
+ this.reloadIfNeeded();
1261
+ const beforeLen = this.database.checkpoints.length;
1262
+ const remaining = this.database.checkpoints.filter((c) => c.id !== id);
1263
+ if (remaining.length === beforeLen) {
1264
+ return false;
1265
+ }
1266
+ this.database = {
1267
+ ...this.database,
1268
+ updatedAt: new Date().toISOString(),
1269
+ latestCheckpointId: remaining[0]?.id || null,
1270
+ totalCheckpoints: remaining.length,
1271
+ checkpoints: remaining
1272
+ };
1273
+ this.saveDatabase();
1274
+ return true;
1275
+ }
1276
+ static formatForAgentResume(checkpoint, compact = false) {
1277
+ if (compact) {
1278
+ return JSON.stringify({
1279
+ ok: true,
1280
+ id: checkpoint.id,
1281
+ sessionId: checkpoint.sessionId,
1282
+ title: checkpoint.title,
1283
+ createdAt: checkpoint.createdAt,
1284
+ cwd: checkpoint.cwd,
1285
+ git: checkpoint.gitBranch ? `${checkpoint.gitBranch}@${checkpoint.gitCommit || "HEAD"}` : undefined,
1286
+ goal: checkpoint.goal,
1287
+ completedSteps: checkpoint.completedSteps,
1288
+ pendingTasks: checkpoint.pendingTasks,
1289
+ keyDecisions: checkpoint.keyDecisions,
1290
+ activeFiles: checkpoint.activeFiles,
1291
+ artifacts: checkpoint.artifacts,
1292
+ activeInvariants: checkpoint.activeInvariants
1293
+ });
1294
+ }
1295
+ const lines = [
1296
+ `### \uD83D\uDCCC [Synapse Context Snapshot \uBCF5\uC6D0] ${checkpoint.title}`,
1297
+ `- **\uCCB4\uD06C\uD3EC\uC778\uD2B8 ID**: \`${checkpoint.id}\` (\uC138\uC158: \`${checkpoint.sessionId}\`, \uC2DC\uAC01: ${checkpoint.createdAt})`,
1298
+ `- **\uC791\uC5C5 \uACBD\uB85C**: \`${checkpoint.cwd}\`${checkpoint.gitBranch ? ` (Git: \`${checkpoint.gitBranch}\` @ \`${checkpoint.gitCommit || "HEAD"}\`)` : ""}`,
1299
+ `- **\uD575\uC2EC \uBAA9\uD45C(Goal)**: ${checkpoint.goal}`
1300
+ ];
1301
+ if (checkpoint.completedSteps.length > 0) {
1302
+ lines.push(`
1303
+ #### \u2705 \uC644\uB8CC\uB41C \uC791\uC5C5 (Completed Steps)`);
1304
+ for (const step of checkpoint.completedSteps) {
1305
+ lines.push(` - ${step}`);
1306
+ }
1307
+ }
1308
+ if (checkpoint.pendingTasks.length > 0) {
1309
+ lines.push(`
1310
+ #### \u23F3 \uBC14\uB85C \uC774\uC5B4\uC11C \uC9C4\uD589\uD560 \uC791\uC5C5 (Next Pending Tasks)`);
1311
+ for (let i = 0;i < checkpoint.pendingTasks.length; i++) {
1312
+ lines.push(` ${i + 1}. **${checkpoint.pendingTasks[i]}**`);
1313
+ }
1314
+ }
1315
+ if (checkpoint.keyDecisions.length > 0) {
1316
+ lines.push(`
1317
+ #### \uD83D\uDCA1 \uD575\uC2EC \uC124\uACC4 \uACB0\uC815 \uBC0F \uB9E5\uB77D (Key Decisions)`);
1318
+ for (const dec of checkpoint.keyDecisions) {
1319
+ lines.push(` - ${dec}`);
1320
+ }
1321
+ }
1322
+ if (checkpoint.artifacts.length > 0) {
1323
+ lines.push(`
1324
+ #### \uD83D\uDCC4 \uCC38\uC870 \uC544\uD2F0\uD329\uD2B8 \uBC0F \uC124\uACC4 \uBB38\uC11C (Artifacts)`);
1325
+ for (const art of checkpoint.artifacts) {
1326
+ lines.push(` - \`${art}\``);
1327
+ }
1328
+ }
1329
+ if (checkpoint.activeFiles.length > 0) {
1330
+ lines.push(`
1331
+ #### \uD83D\uDCC2 \uD575\uC2EC \uC791\uC5C5 \uD30C\uC77C (Active Files)`);
1332
+ lines.push(` - ${checkpoint.activeFiles.map((f) => `\`${f}\``).join(", ")}`);
1333
+ }
1334
+ if (checkpoint.activeInvariants.length > 0) {
1335
+ lines.push(`
1336
+ #### \uD83D\uDEE1\uFE0F \uD65C\uC131 \uBD88\uBCC0 \uADDC\uCE59 (Active Invariants)`);
1337
+ for (const inv of checkpoint.activeInvariants) {
1338
+ lines.push(` - ${inv}`);
1339
+ }
1340
+ }
1341
+ return lines.join(`
1342
+ `);
1343
+ }
1344
+ }
1345
+
1346
+ // memory/causal-timeseries.ts
1347
+ class CausalTimeSeriesEngine {
1348
+ contextStore;
1349
+ constructor(contextStore, _memoryStore) {
1350
+ this.contextStore = contextStore || new ContextCheckpointStore;
1351
+ }
1352
+ static calculateTemporalDecayScore(options) {
1353
+ const baseScore = options.baseScore ?? 1;
1354
+ const nowMs = options.nowMs ?? Date.now();
1355
+ const deltaHours = Math.max(0, (nowMs - options.lastActiveMs) / (1000 * 60 * 60));
1356
+ const halfLifeHours = Math.max(0.1, options.halfLifeHours ?? 24);
1357
+ const lambda = Math.LN2 / halfLifeHours;
1358
+ const hitCount = Math.max(0, options.hitCount ?? 0);
1359
+ const decayFactor = Math.exp(-lambda * deltaHours);
1360
+ const frequencyBoost = 0.25 * Math.log(1 + hitCount);
1361
+ return Number((baseScore * decayFactor + frequencyBoost).toFixed(4));
1362
+ }
1363
+ computeDecayedResonance(rules) {
1364
+ return rules.map((r) => {
1365
+ const lastActiveMs = r.updatedAt ? new Date(r.updatedAt).getTime() : Date.now();
1366
+ const decayScore = CausalTimeSeriesEngine.calculateTemporalDecayScore({
1367
+ baseScore: (r.confidence ?? 1) * (r.weight ?? 1),
1368
+ lastActiveMs
1369
+ });
1370
+ return { ...r, decayScore };
1371
+ });
1372
+ }
1373
+ generateTimeline(options) {
1374
+ const opt = typeof options === "number" ? { limit: options } : options;
1375
+ const checkpoints = this.contextStore.listCheckpoints({
1376
+ sessionId: opt?.sessionId
1377
+ });
1378
+ const nowMs = Date.now();
1379
+ const sinceMs = opt?.sinceMs ?? nowMs - 7 * 24 * 60 * 60 * 1000;
1380
+ const events = [];
1381
+ for (const cp of checkpoints) {
1382
+ const tsMs = new Date(cp.createdAt).getTime();
1383
+ if (tsMs < sinceMs)
1384
+ continue;
1385
+ events.push({
1386
+ id: `tl_${cp.id}`,
1387
+ timestamp: cp.createdAt,
1388
+ timestampMs: tsMs,
1389
+ type: "checkpoint",
1390
+ sessionId: cp.sessionId,
1391
+ title: cp.title,
1392
+ summary: `\uBAA9\uD45C: ${cp.goal} | \uC644\uB8CC: ${cp.completedSteps.length}\uAC74 | \uC794\uC5EC: ${cp.pendingTasks.length}\uAC74`,
1393
+ tags: [cp.sessionId, cp.gitBranch || "main"],
1394
+ checkpointId: cp.id
1395
+ });
1396
+ }
1397
+ events.sort((a, b) => b.timestampMs - a.timestampMs);
1398
+ const limit = opt?.limit && opt.limit > 0 ? opt.limit : events.length;
1399
+ return events.slice(0, limit);
1400
+ }
1401
+ diffCheckpoints(fromId, toId) {
1402
+ const fromCp = this.contextStore.getCheckpoint(fromId);
1403
+ const toCp = this.contextStore.getCheckpoint(toId);
1404
+ if (!fromCp) {
1405
+ throw new Error(`\uC2DC\uC791 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${fromId}`);
1406
+ }
1407
+ if (!toCp) {
1408
+ throw new Error(`\uBE44\uAD50 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${toId}`);
1409
+ }
1410
+ const fromMs = new Date(fromCp.createdAt).getTime();
1411
+ const toMs = new Date(toCp.createdAt).getTime();
1412
+ const deltaHours = Number(Math.abs(toMs - fromMs) / (1000 * 60 * 60)).toFixed(2);
1413
+ const fromPendingSet = new Set(fromCp.pendingTasks);
1414
+ const toPendingSet = new Set(toCp.pendingTasks);
1415
+ const addedPendingTasks = toCp.pendingTasks.filter((t) => !fromPendingSet.has(t));
1416
+ const resolvedTasks = fromCp.pendingTasks.filter((t) => !toPendingSet.has(t));
1417
+ const fromCompletedSet = new Set(fromCp.completedSteps);
1418
+ const newCompletedSteps = toCp.completedSteps.filter((s) => !fromCompletedSet.has(s));
1419
+ const fromDecisionsSet = new Set(fromCp.keyDecisions);
1420
+ const newDecisions = toCp.keyDecisions.filter((d) => !fromDecisionsSet.has(d));
1421
+ const fromFilesSet = new Set(fromCp.modifiedFiles);
1422
+ const toFilesSet = new Set(toCp.modifiedFiles);
1423
+ const addedFiles = toCp.modifiedFiles.filter((f) => !fromFilesSet.has(f));
1424
+ const removedFiles = fromCp.modifiedFiles.filter((f) => !toFilesSet.has(f));
1425
+ const retainedFiles = toCp.modifiedFiles.filter((f) => fromFilesSet.has(f));
1426
+ const fromInvSet = new Set(fromCp.activeInvariants);
1427
+ const toInvSet = new Set(toCp.activeInvariants);
1428
+ const addedInvariants = toCp.activeInvariants.filter((i) => !fromInvSet.has(i));
1429
+ const removedInvariants = fromCp.activeInvariants.filter((i) => !toInvSet.has(i));
1430
+ return {
1431
+ fromId: fromCp.id,
1432
+ toId: toCp.id,
1433
+ fromCheckpointId: fromCp.id,
1434
+ toCheckpointId: toCp.id,
1435
+ fromTimestamp: fromCp.createdAt,
1436
+ toTimestamp: toCp.createdAt,
1437
+ timeDeltaHours: parseFloat(deltaHours),
1438
+ newlyCompletedSteps: newCompletedSteps,
1439
+ resolvedPendingTasks: resolvedTasks,
1440
+ addedModifiedFiles: addedFiles,
1441
+ addedPendingTasks,
1442
+ resolvedTasks,
1443
+ newCompletedSteps,
1444
+ newDecisions,
1445
+ modifiedFilesDelta: {
1446
+ added: addedFiles,
1447
+ removed: removedFiles,
1448
+ retained: retainedFiles
1449
+ },
1450
+ activeInvariantsDelta: {
1451
+ added: addedInvariants,
1452
+ removed: removedInvariants
1453
+ }
1454
+ };
1455
+ }
1456
+ async rewindToCheckpoint(targetIdOrTag) {
1457
+ const targetCp = this.contextStore.getCheckpoint(targetIdOrTag);
1458
+ if (!targetCp) {
1459
+ throw new Error(`\uB418\uAC10\uAE30 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetIdOrTag}`);
1460
+ }
1461
+ const rolledBack = await this.contextStore.saveCheckpoint({
1462
+ sessionId: targetCp.sessionId,
1463
+ title: `[Rewind \uB864\uBC31] ${targetCp.title}`,
1464
+ goal: targetCp.goal,
1465
+ cwd: targetCp.cwd,
1466
+ activeFiles: targetCp.activeFiles,
1467
+ modifiedFiles: targetCp.modifiedFiles,
1468
+ completedSteps: targetCp.completedSteps,
1469
+ pendingTasks: targetCp.pendingTasks,
1470
+ keyDecisions: [
1471
+ `\uACFC\uAC70 \uCCB4\uD06C\uD3EC\uC778\uD2B8(${targetCp.id}, ${targetCp.createdAt})\uB85C \uC2DC\uACC4\uC5F4 \uB418\uAC10\uAE30 \uC218\uD589\uB428`,
1472
+ ...targetCp.keyDecisions
1473
+ ],
1474
+ activeInvariants: targetCp.activeInvariants,
1475
+ artifacts: targetCp.artifacts,
1476
+ autoGit: false,
1477
+ autoResonance: false
1478
+ });
1479
+ return rolledBack;
1480
+ }
1481
+ static formatDiffReport(diff) {
1482
+ const lines = [
1483
+ `### \u23F3 [Synapse \uC2DC\uACC4\uC5F4 \uCC28\uBD84 \uBD84\uC11D] \`${diff.fromCheckpointId}\` \u2794 \`${diff.toCheckpointId}\``,
1484
+ `- **\uACBD\uACFC \uC2DC\uAC04**: \uC57D ${diff.timeDeltaHours}\uC2DC\uAC04 (${diff.fromTimestamp} \u2794 ${diff.toTimestamp})`
1485
+ ];
1486
+ if (diff.resolvedTasks.length > 0) {
1487
+ lines.push(`
1488
+ #### \uD83C\uDFAF \uD574\uACB0/\uC644\uB8CC\uB41C \uACFC\uC81C (Resolved Tasks)`);
1489
+ for (const t of diff.resolvedTasks)
1490
+ lines.push(` - \u2714 ~~${t}~~`);
1491
+ }
1492
+ if (diff.newCompletedSteps.length > 0) {
1493
+ lines.push(`
1494
+ #### \u2728 \uC0C8\uB85C \uC644\uB8CC\uB41C \uB2E8\uACC4 (New Completed Steps)`);
1495
+ for (const s of diff.newCompletedSteps)
1496
+ lines.push(` - \u2705 ${s}`);
1497
+ }
1498
+ if (diff.addedPendingTasks.length > 0) {
1499
+ lines.push(`
1500
+ #### \u23F3 \uC0C8\uB85C \uBD80\uC5EC\uB41C \uB2E4\uC74C \uACFC\uC81C (Added Pending Tasks)`);
1501
+ for (const t of diff.addedPendingTasks)
1502
+ lines.push(` - \uD83D\uDCCC **${t}**`);
1503
+ }
1504
+ if (diff.newDecisions.length > 0) {
1505
+ lines.push(`
1506
+ #### \uD83D\uDCA1 \uC0C8\uB85C\uC6B4 \uC124\uACC4 \uACB0\uC815 (New Key Decisions)`);
1507
+ for (const d of diff.newDecisions)
1508
+ lines.push(` - ${d}`);
1509
+ }
1510
+ if (diff.modifiedFilesDelta.added.length > 0 || diff.modifiedFilesDelta.removed.length > 0) {
1511
+ lines.push(`
1512
+ #### \uD83D\uDCC2 \uD30C\uC77C \uBCC0\uACBD\uC810 (Files Delta)`);
1513
+ if (diff.modifiedFilesDelta.added.length > 0) {
1514
+ lines.push(` - \uD83D\uDFE2 **\uCD94\uAC00**: ${diff.modifiedFilesDelta.added.map((f) => `\`${f}\``).join(", ")}`);
1515
+ }
1516
+ if (diff.modifiedFilesDelta.removed.length > 0) {
1517
+ lines.push(` - \uD83D\uDD34 **\uC81C\uAC70**: ${diff.modifiedFilesDelta.removed.map((f) => `\`${f}\``).join(", ")}`);
1518
+ }
1519
+ }
1520
+ if (diff.activeInvariantsDelta.added.length > 0) {
1521
+ lines.push(`
1522
+ #### \uD83D\uDEE1\uFE0F \uC0C8\uB85C \uBC1C\uB3D9\uB41C \uBD88\uBCC0 \uADDC\uCE59 (New Invariants)`);
1523
+ for (const i of diff.activeInvariantsDelta.added)
1524
+ lines.push(` - ${i}`);
1525
+ }
1526
+ return lines.join(`
1527
+ `);
1528
+ }
1529
+ }
1530
+
1531
+ // memory/causal-graveyard.ts
1532
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, renameSync as renameSync2, statSync as statSync3 } from "fs";
1533
+ import { dirname as dirname3, resolve as resolve2 } from "path";
1534
+ import { homedir as homedir2 } from "os";
1535
+ class CausalGraveyardStore {
1536
+ dbPath;
1537
+ database;
1538
+ lastMtimeMs = 0;
1539
+ constructor(customPathOrMemoryStore) {
1540
+ const customPath = typeof customPathOrMemoryStore === "string" ? customPathOrMemoryStore : undefined;
1541
+ this.dbPath = customPath || process.env.SYNAPSE_GRAVEYARD_PATH || resolve2(homedir2(), ".gemini", "antigravity", "causal_graveyard.json");
1542
+ this.database = this.loadDatabase();
1543
+ }
1544
+ loadDatabase() {
1545
+ try {
1546
+ if (existsSync3(this.dbPath)) {
1547
+ const stat = statSync3(this.dbPath);
1548
+ this.lastMtimeMs = stat.mtimeMs;
1549
+ const raw = readFileSync3(this.dbPath, "utf-8");
1550
+ const parsed = JSON.parse(raw);
1551
+ if (parsed && Array.isArray(parsed.traps)) {
1552
+ return parsed;
1553
+ }
1554
+ }
1555
+ } catch {}
1556
+ return {
1557
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1558
+ updatedAt: new Date().toISOString(),
1559
+ totalTraps: 0,
1560
+ traps: []
1561
+ };
1562
+ }
1563
+ reloadIfNeeded() {
1564
+ try {
1565
+ if (existsSync3(this.dbPath)) {
1566
+ const stat = statSync3(this.dbPath);
1567
+ if (stat.mtimeMs > this.lastMtimeMs) {
1568
+ this.database = this.loadDatabase();
1569
+ }
1570
+ }
1571
+ } catch {}
1572
+ }
1573
+ saveDatabase() {
1574
+ try {
1575
+ const dir = dirname3(this.dbPath);
1576
+ if (!existsSync3(dir)) {
1577
+ mkdirSync3(dir, { recursive: true });
1578
+ }
1579
+ const serialized = JSON.stringify(this.database, null, 2);
1580
+ const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
1581
+ writeFileSync3(tempPath, serialized, "utf-8");
1582
+ renameSync2(tempPath, this.dbPath);
1583
+ try {
1584
+ this.lastMtimeMs = statSync3(this.dbPath).mtimeMs;
1585
+ } catch {}
1586
+ } catch (error) {
1587
+ throw new Error(`\uC2E4\uD328 \uAC00\uC124 \uBB18\uC9C0 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
1588
+ }
1589
+ }
1590
+ assertTrap(input) {
1591
+ this.reloadIfNeeded();
1592
+ const nowIso = new Date().toISOString();
1593
+ const id = input.id || `trap_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
1594
+ const title = input.title || input.trapPattern || "\uC54C \uC218 \uC5C6\uB294 \uD568\uC815 \uD328\uD134";
1595
+ const failedApproach = input.failedApproach || input.trapPattern || title;
1596
+ const rootCause = input.rootCause || input.reason || "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
1597
+ const counterMeasure = input.counterMeasure || input.safeAlternative || "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
1598
+ const triggerKeywords = Array.isArray(input.triggerKeywords) ? input.triggerKeywords : Array.isArray(input.keywords) ? input.keywords : title.split(/\s+/).filter(Boolean);
1599
+ const triggerFiles = Array.isArray(input.triggerFiles) ? input.triggerFiles : Array.isArray(input.avoidFiles) ? input.avoidFiles : Array.isArray(input.files) ? input.files : undefined;
1600
+ const trap = {
1601
+ id,
1602
+ title,
1603
+ failedApproach,
1604
+ rootCause,
1605
+ counterMeasure,
1606
+ triggerKeywords,
1607
+ triggerFiles,
1608
+ errorSignature: input.errorSignature,
1609
+ environment: input.environment || input.domain || "general",
1610
+ hitCount: (input.hitCount ?? 0) + 1,
1611
+ createdAt: input.createdAt || nowIso,
1612
+ lastEncounteredAt: nowIso,
1613
+ trapPattern: failedApproach,
1614
+ domain: input.domain || input.environment || "general",
1615
+ severity: input.severity || (input.errorSignature ? "CRITICAL" : "WARNING"),
1616
+ reason: rootCause,
1617
+ safeAlternative: counterMeasure,
1618
+ avoidFiles: triggerFiles
1619
+ };
1620
+ const filtered = this.database.traps.filter((t) => t.id !== trap.id);
1621
+ const updated = [trap, ...filtered];
1622
+ this.database = {
1623
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1624
+ updatedAt: nowIso,
1625
+ totalTraps: updated.length,
1626
+ traps: updated
1627
+ };
1628
+ this.saveDatabase();
1629
+ return trap;
1630
+ }
1631
+ scanTraps(query) {
1632
+ this.reloadIfNeeded();
1633
+ const warnings = [];
1634
+ const fullText = [
1635
+ query.text || "",
1636
+ query.code || "",
1637
+ ...query.keywords || []
1638
+ ].join(" ").toLowerCase();
1639
+ const files = (query.files || []).map((f) => f.replaceAll("\\", "/").toLowerCase());
1640
+ for (const trap of this.database.traps) {
1641
+ let matched = false;
1642
+ let matchedBy = "keyword";
1643
+ let matchedPattern = "";
1644
+ if (trap.errorSignature && trap.errorSignature.length >= 3) {
1645
+ const sigLower = trap.errorSignature.toLowerCase();
1646
+ if (fullText.includes(sigLower)) {
1647
+ matched = true;
1648
+ matchedBy = "error_signature";
1649
+ matchedPattern = trap.errorSignature;
1650
+ }
1651
+ }
1652
+ const targetFiles = trap.triggerFiles || trap.avoidFiles || [];
1653
+ if (!matched && targetFiles.length > 0) {
1654
+ for (const tf of targetFiles) {
1655
+ const tfLower = tf.replaceAll("\\", "/").toLowerCase();
1656
+ if (files.some((f) => f.includes(tfLower) || tfLower.includes(f))) {
1657
+ matched = true;
1658
+ matchedBy = "file";
1659
+ matchedPattern = tf;
1660
+ break;
1661
+ }
1662
+ }
1663
+ }
1664
+ const targetKeywords = trap.triggerKeywords || [];
1665
+ if (!matched && targetKeywords.length > 0) {
1666
+ for (const kw of targetKeywords) {
1667
+ const kwLower = kw.toLowerCase();
1668
+ if (kwLower.length >= 2 && fullText.includes(kwLower)) {
1669
+ matched = true;
1670
+ matchedBy = "keyword";
1671
+ matchedPattern = kw;
1672
+ break;
1673
+ }
1674
+ }
1675
+ }
1676
+ if (matched) {
1677
+ warnings.push({
1678
+ trapId: trap.id,
1679
+ title: trap.title,
1680
+ riskLevel: trap.severity === "CRITICAL" || trap.errorSignature ? "CRITICAL" : "WARNING",
1681
+ matchedBy,
1682
+ matchedPattern,
1683
+ whyItFails: trap.rootCause,
1684
+ recommendedFix: trap.counterMeasure
1685
+ });
1686
+ }
1687
+ }
1688
+ const hasCritical = warnings.some((w) => w.riskLevel === "CRITICAL");
1689
+ const highestSeverity = warnings.length === 0 ? "NONE" : hasCritical ? "CRITICAL" : "WARNING";
1690
+ const result = warnings;
1691
+ Object.assign(result, {
1692
+ trapped: warnings.length > 0,
1693
+ triggeredTraps: warnings,
1694
+ highestSeverity
1695
+ });
1696
+ return result;
1697
+ }
1698
+ listTraps() {
1699
+ this.reloadIfNeeded();
1700
+ return [...this.database.traps];
1701
+ }
1702
+ getAllTraps() {
1703
+ return this.listTraps();
1704
+ }
1705
+ deleteTrap(id) {
1706
+ this.reloadIfNeeded();
1707
+ const before = this.database.traps.length;
1708
+ const remaining = this.database.traps.filter((t) => t.id !== id);
1709
+ if (remaining.length === before)
1710
+ return false;
1711
+ this.database = {
1712
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1713
+ updatedAt: new Date().toISOString(),
1714
+ totalTraps: remaining.length,
1715
+ traps: remaining
1716
+ };
1717
+ this.saveDatabase();
1718
+ return true;
1719
+ }
1720
+ static formatWarningsForAgent(warnings) {
1721
+ if (!warnings || warnings.length === 0)
1722
+ return "";
1723
+ const lines = [
1724
+ `### \uD83D\uDC80 [\uC2E4\uD328 \uAC00\uC124 \uBB18\uC9C0 \uACBD\uACE0 (Graveyard Trap Detected)] \uC774\uBBF8 \uC2E4\uD328\uD588\uB358 \uC548\uD2F0\uD328\uD134 ${warnings.length}\uAC74 \uAC10\uC9C0!`
1725
+ ];
1726
+ for (const w of warnings) {
1727
+ lines.push(`
1728
+ \uD83D\uDEA8 **[${w.riskLevel}] ${w.title}** (\uAC10\uC9C0: \`${w.matchedPattern}\` by ${w.matchedBy})`);
1729
+ lines.push(` - \u274C **\uC2E4\uD328 \uC6D0\uC778**: ${w.whyItFails}`);
1730
+ lines.push(` - \uD83D\uDCA1 **\uAC80\uC99D\uB41C \uB300\uC548**: ${w.recommendedFix}`);
1731
+ }
1732
+ return lines.join(`
1733
+ `);
1734
+ }
1735
+ static formatTrapWarnings(warnings) {
1736
+ if (!warnings)
1737
+ return "";
1738
+ if (Array.isArray(warnings))
1739
+ return CausalGraveyardStore.formatWarningsForAgent(warnings);
1740
+ if (warnings && Array.isArray(warnings.triggeredTraps)) {
1741
+ return CausalGraveyardStore.formatWarningsForAgent(warnings.triggeredTraps);
1742
+ }
1743
+ return "";
1744
+ }
1745
+ }
1746
+
1747
+ // memory/causal-branch.ts
1748
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, renameSync as renameSync3, statSync as statSync4 } from "fs";
1749
+ import { dirname as dirname4, resolve as resolve3 } from "path";
1750
+ import { homedir as homedir3 } from "os";
1751
+ class CausalBranchStore {
1752
+ dbPath;
1753
+ contextStore;
1754
+ graveyardStore;
1755
+ database;
1756
+ lastMtimeMs = 0;
1757
+ constructor(customPathOrContextStore, contextStoreOrGraveyard, graveyardStore) {
1758
+ if (typeof customPathOrContextStore === "string") {
1759
+ this.dbPath = customPathOrContextStore;
1760
+ this.contextStore = contextStoreOrGraveyard instanceof ContextCheckpointStore ? contextStoreOrGraveyard : new ContextCheckpointStore;
1761
+ this.graveyardStore = graveyardStore || (contextStoreOrGraveyard instanceof CausalGraveyardStore ? contextStoreOrGraveyard : new CausalGraveyardStore);
1762
+ } else if (customPathOrContextStore instanceof ContextCheckpointStore) {
1763
+ this.dbPath = process.env.SYNAPSE_BRANCH_PATH || resolve3(homedir3(), ".gemini", "antigravity", "causal_branches.json");
1764
+ this.contextStore = customPathOrContextStore;
1765
+ this.graveyardStore = contextStoreOrGraveyard instanceof CausalGraveyardStore ? contextStoreOrGraveyard : new CausalGraveyardStore;
1766
+ } else {
1767
+ this.dbPath = process.env.SYNAPSE_BRANCH_PATH || resolve3(homedir3(), ".gemini", "antigravity", "causal_branches.json");
1768
+ this.contextStore = new ContextCheckpointStore;
1769
+ this.graveyardStore = new CausalGraveyardStore;
1770
+ }
1771
+ this.database = this.loadDatabase();
1772
+ }
1773
+ loadDatabase() {
1774
+ try {
1775
+ if (existsSync4(this.dbPath)) {
1776
+ const stat = statSync4(this.dbPath);
1777
+ this.lastMtimeMs = stat.mtimeMs;
1778
+ const raw = readFileSync4(this.dbPath, "utf-8");
1779
+ const parsed = JSON.parse(raw);
1780
+ if (parsed && Array.isArray(parsed.branches)) {
1781
+ return parsed;
1782
+ }
1783
+ }
1784
+ } catch {}
1785
+ return {
1786
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1787
+ updatedAt: new Date().toISOString(),
1788
+ currentBranch: "main",
1789
+ branches: [
1790
+ {
1791
+ name: "main",
1792
+ baseCheckpointId: "root",
1793
+ headCheckpointId: "root",
1794
+ createdAt: new Date().toISOString(),
1795
+ updatedAt: new Date().toISOString(),
1796
+ status: "active",
1797
+ description: "\uAE30\uBCF8 \uBA54\uC778 \uB450\uB1CC \uBE0C\uB79C\uCE58"
1798
+ }
1799
+ ]
1800
+ };
1801
+ }
1802
+ reloadIfNeeded() {
1803
+ try {
1804
+ if (existsSync4(this.dbPath)) {
1805
+ const stat = statSync4(this.dbPath);
1806
+ if (stat.mtimeMs > this.lastMtimeMs) {
1807
+ this.database = this.loadDatabase();
1808
+ }
1809
+ }
1810
+ } catch {}
1811
+ }
1812
+ saveDatabase() {
1813
+ try {
1814
+ const dir = dirname4(this.dbPath);
1815
+ if (!existsSync4(dir)) {
1816
+ mkdirSync4(dir, { recursive: true });
1817
+ }
1818
+ const serialized = JSON.stringify(this.database, null, 2);
1819
+ const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
1820
+ writeFileSync4(tempPath, serialized, "utf-8");
1821
+ renameSync3(tempPath, this.dbPath);
1822
+ try {
1823
+ this.lastMtimeMs = statSync4(this.dbPath).mtimeMs;
1824
+ } catch {}
1825
+ } catch (error) {
1826
+ throw new Error(`\uCEE8\uD14D\uC2A4\uD2B8 \uBE0C\uB79C\uCE58 \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
1827
+ }
1828
+ }
1829
+ async createBranch(name, options) {
1830
+ this.reloadIfNeeded();
1831
+ const cleanName = name.trim();
1832
+ if (!cleanName)
1833
+ throw new Error("\uBE0C\uB79C\uCE58 \uC774\uB984\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
1834
+ const existing = this.database.branches.find((b) => b.name === cleanName);
1835
+ if (existing && existing.status === "active") {
1836
+ throw new Error(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD65C\uC131 \uBE0C\uB79C\uCE58\uC785\uB2C8\uB2E4: ${cleanName}`);
1837
+ }
1838
+ let baseCp = null;
1839
+ if (options?.baseId) {
1840
+ baseCp = this.contextStore.getCheckpoint(options.baseId);
1841
+ } else {
1842
+ baseCp = this.contextStore.getLatestCheckpoint();
1843
+ }
1844
+ const baseId = baseCp?.id || "root";
1845
+ const nowIso = new Date().toISOString();
1846
+ const forkedCp = await this.contextStore.saveCheckpoint({
1847
+ sessionId: `branch-${cleanName}`,
1848
+ title: `[Branch Start] ${cleanName}`,
1849
+ goal: options?.description || `\uBE0C\uB79C\uCE58 ${cleanName} \uB3C5\uB9BD \uAC00\uC124 \uAC80\uC99D`,
1850
+ completedSteps: baseCp?.completedSteps || [],
1851
+ pendingTasks: baseCp?.pendingTasks || [],
1852
+ keyDecisions: baseCp ? [`\uBE0C\uB79C\uCE58 ${cleanName}\uC774 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ${baseId}\uC5D0\uC11C \uBD84\uAE30\uB428`] : [],
1853
+ activeFiles: baseCp?.activeFiles || [],
1854
+ artifacts: baseCp?.artifacts || [],
1855
+ activeInvariants: baseCp?.activeInvariants || [],
1856
+ autoGit: false,
1857
+ autoResonance: false
1858
+ });
1859
+ const newBranch = {
1860
+ name: cleanName,
1861
+ baseCheckpointId: baseId,
1862
+ headCheckpointId: forkedCp.id,
1863
+ createdAt: nowIso,
1864
+ updatedAt: nowIso,
1865
+ status: "active",
1866
+ description: options?.description
1867
+ };
1868
+ const remaining = this.database.branches.filter((b) => b.name !== cleanName);
1869
+ this.database = {
1870
+ ...this.database,
1871
+ updatedAt: nowIso,
1872
+ currentBranch: cleanName,
1873
+ branches: [...remaining, newBranch]
1874
+ };
1875
+ this.saveDatabase();
1876
+ return newBranch;
1877
+ }
1878
+ switchBranch(name) {
1879
+ this.reloadIfNeeded();
1880
+ const clean = name.trim();
1881
+ const target = this.database.branches.find((b) => b.name === clean);
1882
+ if (!target) {
1883
+ throw new Error(`\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uBE0C\uB79C\uCE58\uC785\uB2C8\uB2E4: ${clean}`);
1884
+ }
1885
+ this.database = {
1886
+ ...this.database,
1887
+ updatedAt: new Date().toISOString(),
1888
+ currentBranch: clean
1889
+ };
1890
+ this.saveDatabase();
1891
+ return target;
1892
+ }
1893
+ async mergeBranch(sourceName, targetName = "main") {
1894
+ this.reloadIfNeeded();
1895
+ const src = this.database.branches.find((b) => b.name === sourceName.trim());
1896
+ const tgt = this.database.branches.find((b) => b.name === targetName.trim());
1897
+ if (!src)
1898
+ throw new Error(`\uC18C\uC2A4 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${sourceName}`);
1899
+ if (!tgt)
1900
+ throw new Error(`\uD0C0\uAE43 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetName}`);
1901
+ if (src.status !== "active")
1902
+ throw new Error(`\uC18C\uC2A4 \uBE0C\uB79C\uCE58\uAC00 \uD65C\uC131 \uC0C1\uD0DC\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${src.status}`);
1903
+ const srcCp = this.contextStore.getCheckpoint(src.headCheckpointId);
1904
+ const tgtCp = this.contextStore.getCheckpoint(tgt.headCheckpointId);
1905
+ const mergedTitle = `[Merge] ${src.name} \u2794 ${tgt.name}`;
1906
+ const mergedDecisions = Array.from(new Set([
1907
+ `\uBE0C\uB79C\uCE58 ${src.name}\uC774 ${tgt.name}\uC73C\uB85C \uBCD1\uD569 \uC644\uB8CC\uB428`,
1908
+ ...tgtCp?.keyDecisions || [],
1909
+ ...srcCp?.keyDecisions || []
1910
+ ]));
1911
+ const mergedCompleted = Array.from(new Set([...tgtCp?.completedSteps || [], ...srcCp?.completedSteps || []]));
1912
+ const mergedPending = Array.from(new Set([...srcCp?.pendingTasks || [], ...tgtCp?.pendingTasks || []]));
1913
+ const mergedFiles = Array.from(new Set([...tgtCp?.activeFiles || [], ...srcCp?.activeFiles || []]));
1914
+ const mergedInvariants = Array.from(new Set([...tgtCp?.activeInvariants || [], ...srcCp?.activeInvariants || []]));
1915
+ const resultingCp = await this.contextStore.saveCheckpoint({
1916
+ sessionId: `branch-${tgt.name}`,
1917
+ title: mergedTitle,
1918
+ goal: tgtCp?.goal || srcCp?.goal || mergedTitle,
1919
+ completedSteps: mergedCompleted,
1920
+ pendingTasks: mergedPending,
1921
+ keyDecisions: mergedDecisions,
1922
+ activeFiles: mergedFiles,
1923
+ activeInvariants: mergedInvariants,
1924
+ artifacts: Array.from(new Set([...tgtCp?.artifacts || [], ...srcCp?.artifacts || []])),
1925
+ autoGit: false,
1926
+ autoResonance: false
1927
+ });
1928
+ const nowIso = new Date().toISOString();
1929
+ const updatedSrc = {
1930
+ ...src,
1931
+ status: "merged",
1932
+ mergedInto: tgt.name,
1933
+ updatedAt: nowIso
1934
+ };
1935
+ const updatedTgt = {
1936
+ ...tgt,
1937
+ headCheckpointId: resultingCp.id,
1938
+ updatedAt: nowIso
1939
+ };
1940
+ const remaining = this.database.branches.filter((b) => b.name !== src.name && b.name !== tgt.name);
1941
+ this.database = {
1942
+ ...this.database,
1943
+ updatedAt: nowIso,
1944
+ currentBranch: tgt.name,
1945
+ branches: [...remaining, updatedTgt, updatedSrc]
1946
+ };
1947
+ this.saveDatabase();
1948
+ return { mergedBranch: updatedSrc, resultingCheckpoint: resultingCp };
1949
+ }
1950
+ abandonBranch(name, failureInfo, safeAlternative) {
1951
+ this.reloadIfNeeded();
1952
+ const target = this.database.branches.find((b) => b.name === name.trim());
1953
+ if (!target)
1954
+ throw new Error(`\uD3D0\uAE30\uD560 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${name}`);
1955
+ const infoObj = typeof failureInfo === "string" ? { rootCause: failureInfo, counterMeasure: safeAlternative } : failureInfo || {};
1956
+ const nowIso = new Date().toISOString();
1957
+ const updated = {
1958
+ ...target,
1959
+ status: "abandoned",
1960
+ abandonmentReason: infoObj.rootCause || "\uC2E4\uD5D8\uC801 \uAC00\uC124 \uC2E4\uD328\uB85C \uAE30\uAC01\uB428",
1961
+ updatedAt: nowIso
1962
+ };
1963
+ let trapRecorded = false;
1964
+ if (infoObj.rootCause || infoObj.failedApproach) {
1965
+ this.graveyardStore.assertTrap({
1966
+ id: `trap_branch_${target.name}_${Date.now().toString(36)}`,
1967
+ title: `\uBE0C\uB79C\uCE58 ${target.name} \uAE30\uAC01 \uAC00\uC124`,
1968
+ failedApproach: infoObj.failedApproach || target.description || target.name,
1969
+ errorSignature: infoObj.errorSignature,
1970
+ rootCause: infoObj.rootCause || "\uAC00\uC124 \uAC80\uC99D \uC2E4\uD328",
1971
+ counterMeasure: infoObj.counterMeasure || "\uD574\uB2F9 \uBE0C\uB79C\uCE58 \uC811\uADFC\uBC95 \uD3D0\uAE30 \uBC0F \uB300\uC548 \uBE0C\uB79C\uCE58 \uC218\uB9BD \uD544\uC694",
1972
+ triggerKeywords: [
1973
+ target.name,
1974
+ ...target.description ? target.description.split(/\s+/) : []
1975
+ ]
1976
+ });
1977
+ trapRecorded = true;
1978
+ }
1979
+ const remaining = this.database.branches.filter((b) => b.name !== target.name);
1980
+ this.database = {
1981
+ ...this.database,
1982
+ updatedAt: nowIso,
1983
+ currentBranch: this.database.currentBranch === target.name ? "main" : this.database.currentBranch,
1984
+ branches: [...remaining, updated]
1985
+ };
1986
+ this.saveDatabase();
1987
+ return { branch: updated, trapRecorded, success: true };
1988
+ }
1989
+ listBranches() {
1990
+ this.reloadIfNeeded();
1991
+ return [...this.database.branches];
1992
+ }
1993
+ getCurrentBranch() {
1994
+ this.reloadIfNeeded();
1995
+ const curr = this.database.branches.find((b) => b.name === this.database.currentBranch);
1996
+ return curr || {
1997
+ name: "main",
1998
+ baseCheckpointId: "root",
1999
+ headCheckpointId: "root",
2000
+ createdAt: new Date().toISOString(),
2001
+ updatedAt: new Date().toISOString(),
2002
+ status: "active"
2003
+ };
2004
+ }
2005
+ }
2006
+
2007
+ // memory/causal-predictor.ts
2008
+ class CausalPredictorEngine {
2009
+ memoryStore;
2010
+ contextStore;
2011
+ constructor(memoryStore, contextStore) {
2012
+ this.memoryStore = memoryStore || new CausalMemoryStore;
2013
+ this.contextStore = contextStore || new ContextCheckpointStore;
2014
+ }
2015
+ analyzeBlastRadius(files, maxHops = 3) {
2016
+ const startTime = performance.now();
2017
+ this.memoryStore.reloadIfNeeded();
2018
+ this.contextStore.reloadIfNeeded();
2019
+ const normalizedFiles = files.map((f) => f.replaceAll("\\", "/").toLowerCase());
2020
+ const resonance = this.memoryStore.queryResonance({
2021
+ files: normalizedFiles,
2022
+ propagateRelations: false
2023
+ });
2024
+ const directRules = resonance.resonanceRules;
2025
+ const rawDb = this.memoryStore.getRawDatabase();
2026
+ const outgoingMap = new Map;
2027
+ const incomingMap = new Map;
2028
+ for (const rel of rawDb.relations) {
2029
+ const fromLower = rel.from.toLowerCase();
2030
+ const toLower = rel.to.toLowerCase();
2031
+ if (!outgoingMap.has(fromLower))
2032
+ outgoingMap.set(fromLower, []);
2033
+ outgoingMap.get(fromLower).push(rel);
2034
+ if (!incomingMap.has(toLower))
2035
+ incomingMap.set(toLower, []);
2036
+ incomingMap.get(toLower).push(rel);
2037
+ }
2038
+ const titleToRule = new Map;
2039
+ for (const rule of rawDb.rules) {
2040
+ titleToRule.set(rule.title.toLowerCase(), rule);
2041
+ titleToRule.set(rule.id.toLowerCase(), rule);
2042
+ }
2043
+ const visited = new Set;
2044
+ const affectedNodes = [];
2045
+ const prefetchedInvariantsSet = new Set;
2046
+ const suggestedFilesSet = new Set;
2047
+ for (const direct of directRules) {
2048
+ if (direct.invariants && Array.isArray(direct.invariants)) {
2049
+ for (const inv of direct.invariants) {
2050
+ prefetchedInvariantsSet.add(inv);
2051
+ }
2052
+ }
2053
+ }
2054
+ let currentLevel = directRules.map((r) => r.title.toLowerCase());
2055
+ for (const t of currentLevel)
2056
+ visited.add(t);
2057
+ for (let hop = 1;hop <= maxHops; hop++) {
2058
+ const nextLevel = [];
2059
+ const hopWeight = Math.pow(0.7, hop - 1);
2060
+ for (const currentTitle of currentLevel) {
2061
+ const outRels = outgoingMap.get(currentTitle) || [];
2062
+ const inRels = incomingMap.get(currentTitle) || [];
2063
+ const allRels = [...outRels, ...inRels];
2064
+ for (const rel of allRels) {
2065
+ const neighbor = rel.from.toLowerCase() === currentTitle ? rel.to.toLowerCase() : rel.from.toLowerCase();
2066
+ if (visited.has(neighbor))
2067
+ continue;
2068
+ visited.add(neighbor);
2069
+ nextLevel.push(neighbor);
2070
+ const neighborRule = titleToRule.get(neighbor);
2071
+ if (neighborRule) {
2072
+ let baseScore = 60;
2073
+ const relKind = rel.relation || rel.relationType || "requires";
2074
+ const rType = relKind.toLowerCase();
2075
+ if (rType.includes("trigger") || rType.includes("govern"))
2076
+ baseScore = 90;
2077
+ else if (rType.includes("verify") || rType.includes("produce"))
2078
+ baseScore = 75;
2079
+ const riskScore = Math.round(baseScore * hopWeight);
2080
+ for (const inv of neighborRule.invariants || []) {
2081
+ prefetchedInvariantsSet.add(inv);
2082
+ }
2083
+ if (neighborRule.triggers && Array.isArray(neighborRule.triggers.files)) {
2084
+ for (const f of neighborRule.triggers.files) {
2085
+ suggestedFilesSet.add(f);
2086
+ }
2087
+ }
2088
+ affectedNodes.push({
2089
+ name: neighborRule.title || neighborRule.name || neighborRule.id,
2090
+ domain: neighborRule.domain,
2091
+ hopLevel: hop,
2092
+ relationType: relKind,
2093
+ riskReason: `${hop}\uB2E8\uACC4 \uC778\uACFC \uC5F0\uACB0 (\`${rel.from}\` \u2500\u2500[${relKind}]\u2500\u2500\u25B6 \`${rel.to}\`)`,
2094
+ riskScore,
2095
+ requiredInvariants: neighborRule.invariants || []
2096
+ });
2097
+ }
2098
+ }
2099
+ }
2100
+ if (nextLevel.length === 0)
2101
+ break;
2102
+ currentLevel = nextLevel;
2103
+ }
2104
+ const recentCheckpoints = this.contextStore.listCheckpoints({ limit: 15 });
2105
+ for (const cp of recentCheckpoints) {
2106
+ const hasInputFile = cp.modifiedFiles.some((mf) => normalizedFiles.some((nf) => mf.toLowerCase().includes(nf) || nf.includes(mf.toLowerCase())));
2107
+ if (hasInputFile) {
2108
+ for (const f of cp.modifiedFiles) {
2109
+ if (!normalizedFiles.includes(f.toLowerCase())) {
2110
+ suggestedFilesSet.add(f);
2111
+ }
2112
+ }
2113
+ }
2114
+ }
2115
+ affectedNodes.sort((a, b) => b.riskScore - a.riskScore);
2116
+ const maxRiskScore = affectedNodes.length > 0 ? affectedNodes[0].riskScore : 0;
2117
+ let overallRisk = "LOW";
2118
+ if (maxRiskScore >= 80)
2119
+ overallRisk = "CRITICAL";
2120
+ else if (maxRiskScore >= 60)
2121
+ overallRisk = "HIGH";
2122
+ else if (maxRiskScore >= 40)
2123
+ overallRisk = "MEDIUM";
2124
+ const durationMs = Number((performance.now() - startTime).toFixed(2));
2125
+ return {
2126
+ triggerFiles: files,
2127
+ overallRisk,
2128
+ maxRiskScore,
2129
+ affectedNodes,
2130
+ prefetchedInvariants: Array.from(prefetchedInvariantsSet),
2131
+ suggestedTargetFiles: Array.from(suggestedFilesSet).slice(0, 10),
2132
+ durationMs
2133
+ };
2134
+ }
2135
+ static formatPredictReport(report) {
2136
+ const lines = [
2137
+ `### \uD83D\uDD2E [\uC778\uACFC \uC5F0\uC1C4 \uD30C\uAE09 \uC608\uCE21 \uBD84\uC11D] \uC704\uD5D8\uB3C4: **${report.overallRisk}** (\uCD5C\uACE0 \uC704\uD5D8 \uC810\uC218: ${report.maxRiskScore}/100, \uC18C\uC694: ${report.durationMs}ms)`,
2138
+ `- **\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C**: ${report.triggerFiles.map((f) => `\`${f}\``).join(", ")}`
2139
+ ];
2140
+ if (report.affectedNodes.length > 0) {
2141
+ lines.push(`
2142
+ #### \uD83D\uDCA5 \uC5F0\uC1C4 \uD30C\uAE09 \uC601\uD5A5 \uB178\uB4DC (Blast Radius Nodes)`);
2143
+ for (const node of report.affectedNodes) {
2144
+ lines.push(` - **[${node.riskScore}\uC810 | ${node.hopLevel}\uCC28]** \`${node.name}\` (${node.domain}) \u2794 ${node.riskReason}`);
2145
+ }
2146
+ }
2147
+ if (report.suggestedTargetFiles.length > 0) {
2148
+ lines.push(`
2149
+ #### \uD83C\uDFAF \uD568\uAED8 \uAC80\uC99D/\uC218\uC815\uD574\uC57C \uD560 \uCD94\uCC9C \uD0C0\uAE43 \uD30C\uC77C (Prefetched Targets)`);
2150
+ for (const tf of report.suggestedTargetFiles) {
2151
+ lines.push(` - \`${tf}\``);
2152
+ }
2153
+ }
2154
+ if (report.prefetchedInvariants.length > 0) {
2155
+ lines.push(`
2156
+ #### \uD83D\uDEE1\uFE0F \uC0AC\uC804 \uB85C\uB4DC\uB41C \uD30C\uAE09 \uBD88\uBCC0 \uADDC\uCE59 (Prefetched Invariants)`);
2157
+ for (const inv of report.prefetchedInvariants.slice(0, 8)) {
2158
+ lines.push(` - ${inv}`);
2159
+ }
2160
+ }
2161
+ return lines.join(`
2162
+ `);
2163
+ }
2164
+ }
2165
+
2166
+ // memory/causal-synthesizer.ts
2167
+ var {spawn: spawn3 } = globalThis.Bun;
2168
+ import { existsSync as existsSync5, readdirSync } from "fs";
2169
+ class CausalSynthesizerEngine {
2170
+ contextStore;
2171
+ memoryStore;
2172
+ constructor(contextStore, memoryStore) {
2173
+ this.contextStore = contextStore || new ContextCheckpointStore;
2174
+ this.memoryStore = memoryStore || new CausalMemoryStore;
2175
+ }
2176
+ async inspectGitChanges(cwd = process.cwd()) {
2177
+ try {
2178
+ const gitHead = await detectGitHeadInfo(cwd);
2179
+ const statusProc = spawn3(["git", "status", "--porcelain"], {
2180
+ cwd,
2181
+ stdout: "pipe",
2182
+ stderr: "pipe"
2183
+ });
2184
+ const statusOutput = (await new Response(statusProc.stdout).text()).trim();
2185
+ await statusProc.exited;
2186
+ const modifiedFiles = [];
2187
+ const addedFiles = [];
2188
+ const deletedFiles = [];
2189
+ const untrackedFiles = [];
2190
+ if (statusOutput.length > 0) {
2191
+ for (const line of statusOutput.split(`
2192
+ `)) {
2193
+ const trimmed = line.trimEnd();
2194
+ if (trimmed.length < 3)
2195
+ continue;
2196
+ const statusCode = trimmed.slice(0, 2);
2197
+ const rawFilePath = trimmed.slice(3).trim().replace(/^"|"$/g, "");
2198
+ const normalizedPath = rawFilePath.replaceAll("\\", "/");
2199
+ if (statusCode.includes("M")) {
2200
+ modifiedFiles.push(normalizedPath);
2201
+ } else if (statusCode.includes("A")) {
2202
+ addedFiles.push(normalizedPath);
2203
+ } else if (statusCode.includes("D")) {
2204
+ deletedFiles.push(normalizedPath);
2205
+ } else if (statusCode.includes("?")) {
2206
+ untrackedFiles.push(normalizedPath);
2207
+ }
2208
+ }
2209
+ }
2210
+ const stats = await this.parseGitStats(cwd);
2211
+ const extractedSymbols = await this.extractSymbolsFromDiff(cwd);
2212
+ const allActiveFiles = Array.from(new Set([...modifiedFiles, ...addedFiles, ...untrackedFiles]));
2213
+ const detectedDomains = this.detectDomainsFromFiles(allActiveFiles);
2214
+ return {
2215
+ cwd,
2216
+ branch: gitHead.branch,
2217
+ commit: gitHead.commit,
2218
+ modifiedFiles,
2219
+ addedFiles,
2220
+ deletedFiles,
2221
+ untrackedFiles,
2222
+ allActiveFiles,
2223
+ stats,
2224
+ extractedSymbols,
2225
+ detectedDomains
2226
+ };
2227
+ } catch {
2228
+ return this.fallbackFilesystemScan(cwd);
2229
+ }
2230
+ }
2231
+ async parseGitStats(cwd) {
2232
+ try {
2233
+ const proc = spawn3(["git", "diff", "HEAD", "--shortstat"], {
2234
+ cwd,
2235
+ stdout: "pipe",
2236
+ stderr: "pipe"
2237
+ });
2238
+ const out = (await new Response(proc.stdout).text()).trim();
2239
+ await proc.exited;
2240
+ let filesChanged = 0;
2241
+ let insertions = 0;
2242
+ let deletions = 0;
2243
+ const filesMatch = out.match(/(\d+)\s+files?\s+changed/i);
2244
+ const insertMatch = out.match(/(\d+)\s+insertions?\(\+\)/i);
2245
+ const deleteMatch = out.match(/(\d+)\s+deletions?\(-\)/i);
2246
+ if (filesMatch)
2247
+ filesChanged = parseInt(filesMatch[1], 10);
2248
+ if (insertMatch)
2249
+ insertions = parseInt(insertMatch[1], 10);
2250
+ if (deleteMatch)
2251
+ deletions = parseInt(deleteMatch[1], 10);
2252
+ return { filesChanged, insertions, deletions };
2253
+ } catch {
2254
+ return { filesChanged: 0, insertions: 0, deletions: 0 };
2255
+ }
2256
+ }
2257
+ async extractSymbolsFromDiff(cwd) {
2258
+ const functions = new Set;
2259
+ const classes = new Set;
2260
+ const interfaces = new Set;
2261
+ const types = new Set;
2262
+ try {
2263
+ const proc = spawn3(["git", "diff", "HEAD", "-U1"], {
2264
+ cwd,
2265
+ stdout: "pipe",
2266
+ stderr: "pipe"
2267
+ });
2268
+ const diffText = await new Response(proc.stdout).text();
2269
+ await proc.exited;
2270
+ if (!diffText || diffText.length === 0) {
2271
+ return { functions: [], classes: [], interfaces: [], types: [] };
2272
+ }
2273
+ for (const line of diffText.split(`
2274
+ `)) {
2275
+ if (!line.startsWith("+") && !line.startsWith("@@"))
2276
+ continue;
2277
+ const content = line.startsWith("+") ? line.slice(1).trim() : line;
2278
+ const fnMatch = content.match(/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z0-9_$]+)/) || content.match(/(?:export\s+)?(?:const|let)\s+([A-Za-z0-9_$]+)\s*=\s*(?:async\s*)?\(/);
2279
+ if (fnMatch && fnMatch[1]) {
2280
+ functions.add(fnMatch[1]);
2281
+ }
2282
+ const clsMatch = content.match(/(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z0-9_$]+)/);
2283
+ if (clsMatch && clsMatch[1]) {
2284
+ classes.add(clsMatch[1]);
2285
+ }
2286
+ const ifaceMatch = content.match(/(?:export\s+)?interface\s+([A-Za-z0-9_$]+)/);
2287
+ if (ifaceMatch && ifaceMatch[1]) {
2288
+ interfaces.add(ifaceMatch[1]);
2289
+ }
2290
+ const typeMatch = content.match(/(?:export\s+)?type\s+([A-Za-z0-9_$]+)\s*=/);
2291
+ if (typeMatch && typeMatch[1]) {
2292
+ types.add(typeMatch[1]);
2293
+ }
2294
+ }
2295
+ return {
2296
+ functions: Array.from(functions).slice(0, 15),
2297
+ classes: Array.from(classes).slice(0, 10),
2298
+ interfaces: Array.from(interfaces).slice(0, 10),
2299
+ types: Array.from(types).slice(0, 10)
2300
+ };
2301
+ } catch {
2302
+ return { functions: [], classes: [], interfaces: [], types: [] };
2303
+ }
2304
+ }
2305
+ detectDomainsFromFiles(files) {
2306
+ const domains = new Set;
2307
+ for (const file of files) {
2308
+ const lower = file.toLowerCase();
2309
+ if (lower.includes("test") || lower.endsWith(".test.ts") || lower.endsWith(".spec.ts")) {
2310
+ domains.add("testing");
2311
+ }
2312
+ if (lower.includes("compiler") || lower.includes("ast")) {
2313
+ domains.add("compiler");
2314
+ }
2315
+ if (lower.includes("memory") || lower.includes("causal")) {
2316
+ domains.add("memory");
2317
+ }
2318
+ if (lower.includes("db") || lower.includes("database") || lower.includes("sqlite")) {
2319
+ domains.add("database");
2320
+ }
2321
+ if (lower.includes("mesh") || lower.includes("distributed") || lower.includes("peer")) {
2322
+ domains.add("mesh");
2323
+ }
2324
+ if (lower.includes("runner") || lower.includes("execution")) {
2325
+ domains.add("runner");
2326
+ }
2327
+ if (lower.includes("bus") || lower.includes("event")) {
2328
+ domains.add("bus");
2329
+ }
2330
+ if (lower.includes("guard") || lower.includes("security")) {
2331
+ domains.add("security");
2332
+ }
2333
+ if (lower.includes("studio") || lower.includes("ui") || lower.includes("view")) {
2334
+ domains.add("frontend");
2335
+ }
2336
+ if (lower.includes("lens") || lower.includes("obsidian")) {
2337
+ domains.add("obsidian-lens");
2338
+ }
2339
+ }
2340
+ if (domains.size === 0) {
2341
+ domains.add("core");
2342
+ }
2343
+ return Array.from(domains);
2344
+ }
2345
+ fallbackFilesystemScan(cwd) {
2346
+ try {
2347
+ const modified = [];
2348
+ if (existsSync5(cwd)) {
2349
+ const entries = readdirSync(cwd, { withFileTypes: true });
2350
+ for (const entry of entries) {
2351
+ if (entry.isFile() && !entry.name.startsWith(".")) {
2352
+ modified.push(entry.name);
2353
+ }
2354
+ }
2355
+ }
2356
+ return {
2357
+ cwd,
2358
+ modifiedFiles: modified,
2359
+ addedFiles: [],
2360
+ deletedFiles: [],
2361
+ untrackedFiles: [],
2362
+ allActiveFiles: modified,
2363
+ stats: { insertions: 0, deletions: 0, filesChanged: modified.length },
2364
+ extractedSymbols: { functions: [], classes: [], interfaces: [], types: [] },
2365
+ detectedDomains: ["general"]
2366
+ };
2367
+ } catch {
2368
+ return {
2369
+ cwd,
2370
+ modifiedFiles: [],
2371
+ addedFiles: [],
2372
+ deletedFiles: [],
2373
+ untrackedFiles: [],
2374
+ allActiveFiles: [],
2375
+ stats: { insertions: 0, deletions: 0, filesChanged: 0 },
2376
+ extractedSymbols: { functions: [], classes: [], interfaces: [], types: [] },
2377
+ detectedDomains: ["unknown"]
2378
+ };
2379
+ }
2380
+ }
2381
+ synthesizeIntent(summary, customTitle) {
2382
+ const completedSteps = [];
2383
+ const pendingTasks = [];
2384
+ const keyDecisions = [];
2385
+ const activeInvariants = [];
2386
+ const suggestedKeywords = [...summary.detectedDomains];
2387
+ if (summary.extractedSymbols.classes.length > 0) {
2388
+ completedSteps.push(`\uD575\uC2EC \uD074\uB798\uC2A4 \uBAA8\uB4C8\uD654 \uBC0F \uC120\uC5B8: ${summary.extractedSymbols.classes.join(", ")}`);
2389
+ }
2390
+ if (summary.extractedSymbols.interfaces.length > 0) {
2391
+ completedSteps.push(`\uD0C0\uC785 \uACC4\uC57D \uBC0F \uC778\uD130\uD398\uC774\uC2A4 \uC815\uC758: ${summary.extractedSymbols.interfaces.join(", ")}`);
2392
+ }
2393
+ if (summary.extractedSymbols.functions.length > 0) {
2394
+ completedSteps.push(`\uC8FC\uC694 \uD568\uC218 \uBC0F \uC54C\uACE0\uB9AC\uC998 \uAD6C\uD604: ${summary.extractedSymbols.functions.slice(0, 5).join(", ")}`);
2395
+ }
2396
+ if (summary.detectedDomains.includes("testing")) {
2397
+ completedSteps.push("\uB2E8\uC704 \uBC0F \uD1B5\uD569 \uAC80\uC99D \uD14C\uC2A4\uD2B8 \uC2A4\uC704\uD2B8 \uC791\uC131 \uBC0F \uC815\uBC00 \uAC80\uC99D \uC900\uBE44");
2398
+ }
2399
+ if (summary.detectedDomains.includes("memory")) {
2400
+ completedSteps.push("\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uBC0F \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uC544\uD0A4\uD14D\uCC98 \uBC18\uC601");
2401
+ activeInvariants.push("\uBA54\uBAA8\uB9AC \uCFFC\uB9AC \uC2DC 0ms \uC989\uAC01 \uC751\uB2F5 \uBC0F \uBB34\uC190\uC2E4 \uC778\uACFC\uC728 \uBCF4\uC874");
2402
+ }
2403
+ if (summary.detectedDomains.includes("obsidian-lens")) {
2404
+ completedSteps.push("\uC635\uC2DC\uB514\uC5B8 \uB9C8\uD06C\uB2E4\uC6B4 \uBCFC\uD2B8 \uBC0F \uC2DC\uAC01\uC801 \uCE94\uBC84\uC2A4 \uC5F0\uB3D9 \uACC4\uCE35 \uAD6C\uCD95");
2405
+ }
2406
+ if (summary.addedFiles.length > 0) {
2407
+ completedSteps.push(`\uC2E0\uADDC \uC544\uD0A4\uD14D\uCC98 \uD30C\uC77C \uC0DD\uC131 (${summary.addedFiles.length}\uAC1C \uD30C\uC77C: ${summary.addedFiles.slice(0, 3).join(", ")}${summary.addedFiles.length > 3 ? " \uC678" : ""})`);
2408
+ }
2409
+ if (summary.modifiedFiles.length > 0) {
2410
+ completedSteps.push(`\uAE30\uC874 \uBAA8\uB4C8 \uB9AC\uD329\uD130\uB9C1 \uBC0F \uAE30\uB2A5 \uD655\uC7A5 (${summary.modifiedFiles.length}\uAC1C \uD30C\uC77C)`);
2411
+ }
2412
+ if (completedSteps.length === 0) {
2413
+ completedSteps.push("\uC18C\uC2A4 \uCF54\uB4DC \uBCC0\uACBD \uBC0F \uBAA8\uB4C8 \uAD6C\uC870 \uC815\uBE44");
2414
+ }
2415
+ const hasTests = summary.allActiveFiles.some((f) => f.includes("test") || f.endsWith(".test.ts"));
2416
+ if (!hasTests) {
2417
+ pendingTasks.push("\uCD94\uAC00/\uC218\uC815\uB41C \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uD3EC\uAD04\uC801 \uD1B5\uD569 \uD14C\uC2A4\uD2B8 \uC2A4\uC704\uD2B8 \uC791\uC131 \uBC0F \uD68C\uADC0 \uBC29\uC9C0");
2418
+ }
2419
+ pendingTasks.push("Bun \uB7F0\uD0C0\uC784 \uAE30\uBC18 \uBE4C\uB4DC \uAC80\uC99D (`bun run build`) \uBC0F \uBB34\uC624\uB958 \uD655\uC778");
2420
+ pendingTasks.push("\uC804\uC5ED \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uB3D9\uAE30\uD654 \uBC0F \uAE30\uC5B5 \uC601\uC18D\uD654");
2421
+ keyDecisions.push("\uD558\uB4DC\uCF54\uB529 \uBC30\uC81C \uBC0F \uD658\uACBD \uBCC0\uC218/\uC124\uC815 \uBAA8\uB4C8 \uC911\uC2EC\uC758 \uC124\uC815 \uC544\uD0A4\uD14D\uCC98 \uC801\uC6A9");
2422
+ keyDecisions.push("SOLID \uB2E8\uC77C \uCC45\uC784 \uC6D0\uCE59(SRP)\uC744 \uC900\uC218\uD55C \uB3C5\uB9BD \uC5D4\uC9C4 \uBD84\uB9AC");
2423
+ if (summary.detectedDomains.includes("testing")) {
2424
+ keyDecisions.push("\uBAA8\uC758(Mock)\uB97C \uC9C0\uC591\uD558\uACE0 \uC2E4\uC81C \uACA9\uB9AC \uD658\uACBD\uC5D0\uC11C\uC758 End-to-End \uB3D9\uC791 \uAC80\uC99D \uC218\uD589");
2425
+ }
2426
+ const primaryDomain = summary.detectedDomains[0] || "core";
2427
+ const autoTitle = customTitle || `[\uC790\uB3D9 \uC5ED\uD569\uC131] ${primaryDomain.toUpperCase()} \uBAA8\uB4C8 \uC544\uD0A4\uD14D\uCC98 \uBCC0\uACBD (${summary.stats.filesChanged || summary.allActiveFiles.length}\uAC1C \uD30C\uC77C \uBC18\uC601)`;
2428
+ const autoGoal = `${summary.detectedDomains.join(", ")} \uC601\uC5ED\uC758 \uAE30\uB2A5\uC744 \uD655\uC7A5\uD558\uACE0 \uBCC0\uACBD \uC0AC\uD56D\uC744 \uBB34\uACB0\uD558\uAC8C \uC815\uD569\uD558\uB294 \uC791\uC5C5`;
2429
+ return {
2430
+ title: autoTitle,
2431
+ goal: autoGoal,
2432
+ completedSteps,
2433
+ pendingTasks,
2434
+ keyDecisions,
2435
+ activeInvariants,
2436
+ suggestedKeywords
2437
+ };
2438
+ }
2439
+ async autoCheckpoint(cwd = process.cwd(), options = {}) {
2440
+ const summary = await this.inspectGitChanges(cwd);
2441
+ const synthesized = this.synthesizeIntent(summary, options.title);
2442
+ const mergedCompletedSteps = [
2443
+ ...synthesized.completedSteps,
2444
+ ...options.extraCompletedSteps || []
2445
+ ];
2446
+ const mergedPendingTasks = [
2447
+ ...synthesized.pendingTasks,
2448
+ ...options.extraPendingTasks || []
2449
+ ];
2450
+ const mergedKeyDecisions = [
2451
+ ...synthesized.keyDecisions,
2452
+ ...options.extraKeyDecisions || []
2453
+ ];
2454
+ const mergedInvariants = [
2455
+ ...synthesized.activeInvariants,
2456
+ ...options.extraInvariants || []
2457
+ ];
2458
+ const saveInput = {
2459
+ title: options.title || synthesized.title,
2460
+ goal: options.goal || synthesized.goal,
2461
+ cwd,
2462
+ activeFiles: summary.allActiveFiles,
2463
+ modifiedFiles: [...summary.modifiedFiles, ...summary.addedFiles],
2464
+ completedSteps: mergedCompletedSteps,
2465
+ pendingTasks: mergedPendingTasks,
2466
+ keyDecisions: mergedKeyDecisions,
2467
+ activeInvariants: mergedInvariants,
2468
+ keywords: synthesized.suggestedKeywords,
2469
+ domain: summary.detectedDomains[0] || "core",
2470
+ autoGit: true,
2471
+ autoResonance: options.autoResonance ?? true,
2472
+ customMeta: {
2473
+ stats: summary.stats,
2474
+ symbols: summary.extractedSymbols,
2475
+ synthesizedAt: new Date().toISOString(),
2476
+ ...options.customMeta
2477
+ }
2478
+ };
2479
+ return this.contextStore.saveCheckpoint(saveInput);
2480
+ }
2481
+ }
2482
+
2483
+ // memory/causal-lens.ts
2484
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync as renameSync4 } from "fs";
2485
+ import { resolve as resolve4, basename } from "path";
2486
+ class CausalObsidianLensEngine {
2487
+ memoryStore;
2488
+ contextStore;
2489
+ graveyardStore;
2490
+ timeSeriesEngine;
2491
+ constructor(memoryStore, contextStore, graveyardStore, timeSeriesEngine) {
2492
+ this.memoryStore = memoryStore || new CausalMemoryStore;
2493
+ this.contextStore = contextStore || new ContextCheckpointStore;
2494
+ this.graveyardStore = graveyardStore || new CausalGraveyardStore;
2495
+ this.timeSeriesEngine = timeSeriesEngine || new CausalTimeSeriesEngine(this.contextStore, this.memoryStore);
2496
+ }
2497
+ async exportToVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
2498
+ const startTime = performance.now();
2499
+ try {
2500
+ this.memoryStore.reloadIfNeeded();
2501
+ this.contextStore.reloadIfNeeded();
2502
+ this.graveyardStore.reloadIfNeeded();
2503
+ const mentalRoot = resolve4(vaultRoot, CAUSAL_MEMORY_CONFIG.OBSIDIAN_LENS_SUBDIR);
2504
+ const rulesDir = resolve4(mentalRoot, "Rules");
2505
+ const graveyardDir = resolve4(mentalRoot, "Graveyard");
2506
+ const checkpointsDir = resolve4(mentalRoot, "Checkpoints");
2507
+ mkdirSync5(rulesDir, { recursive: true });
2508
+ mkdirSync5(graveyardDir, { recursive: true });
2509
+ mkdirSync5(checkpointsDir, { recursive: true });
2510
+ const rawDb = this.memoryStore.getRawDatabase();
2511
+ let exportedRules = 0;
2512
+ for (const rule of rawDb.rules) {
2513
+ const ruleTitle = rule.title || rule.name || rule.id;
2514
+ const safeFileName = this.sanitizeFileName(ruleTitle);
2515
+ const filePath = resolve4(rulesDir, `${safeFileName}.md`);
2516
+ const content = this.renderRuleToMarkdown(rule);
2517
+ this.atomicWriteFile(filePath, content);
2518
+ exportedRules++;
2519
+ }
2520
+ const allTraps = this.graveyardStore.listTraps();
2521
+ let exportedGraveyard = 0;
2522
+ for (const trap of allTraps) {
2523
+ const trapTitle = trap.title || trap.failedApproach || trap.id;
2524
+ const safeFileName = this.sanitizeFileName(trapTitle);
2525
+ const filePath = resolve4(graveyardDir, `${safeFileName}.md`);
2526
+ const content = this.renderTrapToMarkdown(trap);
2527
+ this.atomicWriteFile(filePath, content);
2528
+ exportedGraveyard++;
2529
+ }
2530
+ const checkpoints = this.contextStore.listCheckpoints({ limit: 100 });
2531
+ let exportedCheckpoints = 0;
2532
+ for (const cp of checkpoints) {
2533
+ const filePath = resolve4(checkpointsDir, `${cp.id}.md`);
2534
+ const content = this.renderCheckpointToMarkdown(cp);
2535
+ this.atomicWriteFile(filePath, content);
2536
+ exportedCheckpoints++;
2537
+ }
2538
+ const timelinePath = resolve4(mentalRoot, "Timeline.md");
2539
+ const timelineContent = this.renderTimelineMarkdown();
2540
+ this.atomicWriteFile(timelinePath, timelineContent);
2541
+ const canvasPath = resolve4(mentalRoot, "Causal-Architecture.canvas");
2542
+ const canvasDoc = this.generateCanvasDocument(rawDb.rules, rawDb.relations);
2543
+ this.atomicWriteFile(canvasPath, JSON.stringify(canvasDoc, null, 2));
2544
+ const durationMs = Number((performance.now() - startTime).toFixed(2));
2545
+ return {
2546
+ success: true,
2547
+ vaultPath: mentalRoot,
2548
+ exportedRules,
2549
+ exportedGraveyard,
2550
+ exportedCheckpoints,
2551
+ canvasGenerated: true,
2552
+ timelineGenerated: true,
2553
+ durationMs
2554
+ };
2555
+ } catch (err) {
2556
+ const durationMs = Number((performance.now() - startTime).toFixed(2));
2557
+ const message = err instanceof Error ? err.message : String(err);
2558
+ return {
2559
+ success: false,
2560
+ vaultPath: vaultRoot,
2561
+ exportedRules: 0,
2562
+ exportedGraveyard: 0,
2563
+ exportedCheckpoints: 0,
2564
+ canvasGenerated: false,
2565
+ timelineGenerated: false,
2566
+ durationMs,
2567
+ error: `\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uB0B4\uBCF4\uB0B4\uAE30 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${message}`
2568
+ };
2569
+ }
2570
+ }
2571
+ async importFromVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
2572
+ const startTime = performance.now();
2573
+ try {
2574
+ const mentalRoot = resolve4(vaultRoot, CAUSAL_MEMORY_CONFIG.OBSIDIAN_LENS_SUBDIR);
2575
+ const rulesDir = resolve4(mentalRoot, "Rules");
2576
+ const graveyardDir = resolve4(mentalRoot, "Graveyard");
2577
+ let importedRules = 0;
2578
+ let importedGraveyard = 0;
2579
+ if (existsSync6(rulesDir)) {
2580
+ const ruleFiles = readdirSync2(rulesDir).filter((f) => f.endsWith(".md"));
2581
+ for (const file of ruleFiles) {
2582
+ const filePath = resolve4(rulesDir, file);
2583
+ const rawText = readFileSync5(filePath, "utf-8");
2584
+ const parsed = this.parseRuleMarkdown(rawText, basename(file, ".md"));
2585
+ if (parsed) {
2586
+ this.memoryStore.assertRule(parsed);
2587
+ importedRules++;
2588
+ }
2589
+ }
2590
+ }
2591
+ if (existsSync6(graveyardDir)) {
2592
+ const trapFiles = readdirSync2(graveyardDir).filter((f) => f.endsWith(".md"));
2593
+ for (const file of trapFiles) {
2594
+ const filePath = resolve4(graveyardDir, file);
2595
+ const rawText = readFileSync5(filePath, "utf-8");
2596
+ const parsed = this.parseTrapMarkdown(rawText, basename(file, ".md"));
2597
+ if (parsed) {
2598
+ this.graveyardStore.assertTrap(parsed);
2599
+ importedGraveyard++;
2600
+ }
2601
+ }
2602
+ }
2603
+ const durationMs = Number((performance.now() - startTime).toFixed(2));
2604
+ return {
2605
+ success: true,
2606
+ vaultPath: mentalRoot,
2607
+ importedRules,
2608
+ importedGraveyard,
2609
+ durationMs
2610
+ };
2611
+ } catch (err) {
2612
+ const durationMs = Number((performance.now() - startTime).toFixed(2));
2613
+ const message = err instanceof Error ? err.message : String(err);
2614
+ return {
2615
+ success: false,
2616
+ vaultPath: vaultRoot,
2617
+ importedRules: 0,
2618
+ importedGraveyard: 0,
2619
+ durationMs,
2620
+ error: `\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uAC00\uC838\uC624\uAE30 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${message}`
2621
+ };
2622
+ }
2623
+ }
2624
+ async syncWithVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
2625
+ const importResult = await this.importFromVault(vaultRoot);
2626
+ const exportResult = await this.exportToVault(vaultRoot);
2627
+ return {
2628
+ success: importResult.success && exportResult.success,
2629
+ exportResult,
2630
+ importResult,
2631
+ syncedAt: new Date().toISOString()
2632
+ };
2633
+ }
2634
+ renderRuleToMarkdown(rule) {
2635
+ const title = rule.title || rule.name || rule.id;
2636
+ const lines = [
2637
+ "---",
2638
+ `id: "${rule.id}"`,
2639
+ `name: "${title.replaceAll('"', "\\\"")}"`,
2640
+ `domain: "${rule.domain}"`,
2641
+ `confidence: ${rule.confidence ?? 1}`,
2642
+ `weight: ${rule.weight ?? 1}`,
2643
+ `updatedAt: "${rule.updatedAt || new Date().toISOString()}"`,
2644
+ "invariants:",
2645
+ ...(rule.invariants || []).map((inv) => ` - "${inv.replaceAll('"', "\\\"")}"`),
2646
+ "tags:",
2647
+ ' - "synapse/causal-rule"',
2648
+ ` - "domain/${rule.domain}"`,
2649
+ "---",
2650
+ "",
2651
+ `# ${title}`,
2652
+ "",
2653
+ `> [!NOTE] \uC778\uACFC\uC728 \uADDC\uCE59 \uBA54\uD0C0\uB370\uC774\uD130`,
2654
+ `> - **\uB3C4\uBA54\uC778**: \`${rule.domain}\``,
2655
+ `> - **\uC2E0\uB8B0\uB3C4**: \`${rule.confidence ?? 1}\` | **\uAC00\uC911\uCE58**: \`${rule.weight ?? 1}\``,
2656
+ `> - **\uCD5C\uC885 \uAC31\uC2E0**: \`${rule.updatedAt || "N/A"}\``,
2657
+ "",
2658
+ "## \uD83D\uDCA1 \uC694\uC57D \uC124\uBA85",
2659
+ rule.description || "\uC0C1\uC138 \uC124\uBA85 \uC5C6\uC74C",
2660
+ "",
2661
+ "## \uD83D\uDEE1\uFE0F \uD544\uC218 \uBD88\uBCC0\uC2DD (Invariants)"
2662
+ ];
2663
+ if (!rule.invariants || rule.invariants.length === 0) {
2664
+ lines.push("- *\uB4F1\uB85D\uB41C \uBD88\uBCC0 \uC870\uAC74 \uC5C6\uC74C*");
2665
+ } else {
2666
+ for (const inv of rule.invariants) {
2667
+ lines.push(`- [x] ${inv}`);
2668
+ }
2669
+ }
2670
+ if (rule.pattern) {
2671
+ lines.push("", "## \uD83D\uDD0D \uB9E4\uCE6D \uD328\uD134", `\`\`\`regex
2672
+ ${rule.pattern}
2673
+ \`\`\``);
2674
+ }
2675
+ return lines.join(`
2676
+ `);
2677
+ }
2678
+ renderTrapToMarkdown(trap) {
2679
+ const title = trap.title || trap.failedApproach || trap.id;
2680
+ const severity = trap.severity || "WARNING";
2681
+ const rootCause = trap.rootCause || trap.reason || "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
2682
+ const counterMeasure = trap.counterMeasure || trap.safeAlternative || "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
2683
+ const avoidFiles = trap.triggerFiles || trap.avoidFiles || [];
2684
+ return [
2685
+ "---",
2686
+ `id: "${trap.id}"`,
2687
+ `title: "${title.replaceAll('"', "\\\"")}"`,
2688
+ `domain: "${trap.domain || trap.environment || "general"}"`,
2689
+ `severity: "${severity}"`,
2690
+ `createdAt: "${trap.createdAt}"`,
2691
+ "tags:",
2692
+ ' - "synapse/anti-pattern"',
2693
+ ` - "severity/${severity.toLowerCase()}"`,
2694
+ "---",
2695
+ "",
2696
+ `# \u26A0\uFE0F \uD568\uC815 \uACBD\uBCF4: ${title}`,
2697
+ "",
2698
+ `> [!WARNING] \uC704\uD5D8\uB3C4: **${severity}** (\uB3C4\uBA54\uC778: \`${trap.domain || "general"}\`)`,
2699
+ `> \uC774 \uD328\uD134\uC740 \uC774\uC804\uC5D0 \uC2E4\uD328\uB97C \uC720\uBC1C\uD588\uB358 \uC548\uD2F0\uD328\uD134\uC785\uB2C8\uB2E4. \uB3D9\uC77C\uD55C \uC2DC\uB3C4\uB97C \uCC28\uB2E8\uD574\uC57C \uD569\uB2C8\uB2E4.`,
2700
+ "",
2701
+ "## \uD83D\uDCA5 \uC2E4\uD328 \uC6D0\uC778 (Root Cause)",
2702
+ rootCause,
2703
+ "",
2704
+ "## \u2705 \uC548\uC804\uD55C \uB300\uC548 (Safe Solution)",
2705
+ counterMeasure,
2706
+ "",
2707
+ "## \uD83E\uDEA4 \uAC10\uC9C0 \uD30C\uC77C \uD328\uD134",
2708
+ ...avoidFiles.length > 0 ? avoidFiles.map((f) => `- \`${f}\``) : ["- *\uC804\uC5ED \uAC10\uC9C0*"]
2709
+ ].join(`
2710
+ `);
2711
+ }
2712
+ renderCheckpointToMarkdown(cp) {
2713
+ return [
2714
+ "---",
2715
+ `id: "${cp.id}"`,
2716
+ `title: "${cp.title.replaceAll('"', "\\\"")}"`,
2717
+ `createdAt: "${cp.createdAt}"`,
2718
+ `cwd: "${cp.cwd.replaceAll("\\", "/")}"`,
2719
+ `branch: "${cp.gitBranch || "unknown"}"`,
2720
+ `commit: "${cp.gitCommit || "unknown"}"`,
2721
+ "tags:",
2722
+ ' - "synapse/checkpoint"',
2723
+ "---",
2724
+ "",
2725
+ `# \uD83D\uDCCD \uCCB4\uD06C\uD3EC\uC778\uD2B8: ${cp.title}`,
2726
+ "",
2727
+ `> [!INFO] \uC2A4\uB0C5\uC0F7 \uC815\uBCF4`,
2728
+ `> - **ID**: \`${cp.id}\``,
2729
+ `> - **\uAE30\uB85D \uC2DC\uAC01**: \`${cp.createdAt}\``,
2730
+ `> - **Git \uBE0C\uB79C\uCE58**: \`${cp.gitBranch || "N/A"}\` (\`${cp.gitCommit || "N/A"}\`)`,
2731
+ `> - **\uC791\uC5C5 \uB514\uB809\uD130\uB9AC**: \`${cp.cwd}\``,
2732
+ "",
2733
+ "## \uD83C\uDFAF \uC138\uC158 \uBAA9\uD45C",
2734
+ cp.goal,
2735
+ "",
2736
+ "## \u2705 \uC644\uB8CC\uB41C \uB2E8\uACC4 (Completed Steps)",
2737
+ ...cp.completedSteps.map((s) => `- [x] ${s}`),
2738
+ "",
2739
+ "## \u23F3 \uB300\uAE30 \uACFC\uC81C (Pending Tasks)",
2740
+ ...cp.pendingTasks.map((t) => `- [ ] ${t}`),
2741
+ "",
2742
+ "## \uD83E\uDDE0 \uD575\uC2EC \uACB0\uC815 \uC0AC\uD56D (Key Decisions)",
2743
+ ...cp.keyDecisions.map((d) => `- ${d}`),
2744
+ "",
2745
+ "## \uD83D\uDEE1\uFE0F \uD65C\uC131 \uBD88\uBCC0\uC2DD (Active Invariants)",
2746
+ ...cp.activeInvariants.map((inv) => `- \uD83D\uDD12 ${inv}`),
2747
+ "",
2748
+ "## \uD83D\uDCC1 \uC218\uC815\uB41C \uD30C\uC77C \uBAA9\uB85D",
2749
+ ...cp.modifiedFiles.map((f) => `- \`${f}\``)
2750
+ ].join(`
2751
+ `);
2752
+ }
2753
+ renderTimelineMarkdown() {
2754
+ const events = this.timeSeriesEngine.generateTimeline(50);
2755
+ const lines = [
2756
+ "---",
2757
+ 'title: "Synapse Causal Memory Timeline"',
2758
+ "tags:",
2759
+ ' - "synapse/timeline"',
2760
+ "---",
2761
+ "",
2762
+ "# \u23F3 \uC2DC\uB0C5\uC2A4 \uC778\uACFC\uC728 \uD0C0\uC784\uB77C\uC778",
2763
+ "",
2764
+ `> \uCD1D **${events.length}**\uAC1C\uC758 \uC774\uBCA4\uD2B8\uAC00 \uBCF4\uC874\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`,
2765
+ "",
2766
+ "## \uD83D\uDCCA \uD0C0\uC784\uB77C\uC778 \uC2DC\uAC01\uD654 (Mermaid)",
2767
+ "```mermaid",
2768
+ "timeline",
2769
+ " title \uC138\uC158 \uC9C4\uD654 \uD0C0\uC784\uB77C\uC778"
2770
+ ];
2771
+ for (const ev of events.slice(-10)) {
2772
+ const datePart = ev.timestamp ? ev.timestamp.slice(0, 10) : "2026-11-09";
2773
+ const safeTitle = (ev.title || "\uC774\uBCA4\uD2B8").replaceAll('"', "").replaceAll(":", " -");
2774
+ lines.push(` ${datePart} : ${safeTitle}`);
2775
+ }
2776
+ lines.push("```", "", "## \uD83D\uDCCB \uC774\uBCA4\uD2B8 \uC774\uB825");
2777
+ for (const ev of events) {
2778
+ const targetLink = ev.checkpointId ? `./Checkpoints/${ev.checkpointId}.md` : "#";
2779
+ lines.push(`### [${ev.title}](${targetLink})`);
2780
+ lines.push(`- **\uBC1C\uC0DD \uC77C\uC2DC**: \`${ev.timestamp}\``);
2781
+ lines.push(`- **\uC694\uC57D**: ${ev.summary}`);
2782
+ lines.push("");
2783
+ }
2784
+ return lines.join(`
2785
+ `);
2786
+ }
2787
+ generateCanvasDocument(rules, relations) {
2788
+ const nodes = [];
2789
+ const edges = [];
2790
+ const ruleIdToNodeId = new Map;
2791
+ const nodeWidth = 320;
2792
+ const nodeHeight = 160;
2793
+ const spacingX = 400;
2794
+ const spacingY = 220;
2795
+ const cols = 4;
2796
+ rules.forEach((rule, idx) => {
2797
+ const title = rule.title || rule.name || rule.id;
2798
+ const col = idx % cols;
2799
+ const row = Math.floor(idx / cols);
2800
+ const nodeId = `rule_node_${idx}`;
2801
+ ruleIdToNodeId.set(title.toLowerCase(), nodeId);
2802
+ ruleIdToNodeId.set(rule.id.toLowerCase(), nodeId);
2803
+ const summaryInvariants = rule.invariants && rule.invariants.length > 0 ? rule.invariants.slice(0, 2).map((i) => `\u2022 ${i}`).join(`
2804
+ `) : "\u2022 \uBD88\uBCC0\uC2DD \uC5C6\uC74C";
2805
+ const markdownText = [
2806
+ `### \uD83D\uDCCC [[Rules/${this.sanitizeFileName(title)}|${title}]]`,
2807
+ `**\uB3C4\uBA54\uC778**: \`${rule.domain}\` | **\uAC00\uC911\uCE58**: \`${rule.weight ?? 1}\``,
2808
+ "",
2809
+ summaryInvariants
2810
+ ].join(`
2811
+ `);
2812
+ nodes.push({
2813
+ id: nodeId,
2814
+ x: col * spacingX,
2815
+ y: row * spacingY,
2816
+ width: nodeWidth,
2817
+ height: nodeHeight,
2818
+ type: "text",
2819
+ text: markdownText,
2820
+ color: this.getColorForDomain(rule.domain)
2821
+ });
2822
+ });
2823
+ let edgeCounter = 0;
2824
+ for (const rel of relations) {
2825
+ const fromNodeId = ruleIdToNodeId.get(rel.from.toLowerCase());
2826
+ const toNodeId = ruleIdToNodeId.get(rel.to.toLowerCase());
2827
+ if (fromNodeId && toNodeId) {
2828
+ edges.push({
2829
+ id: `edge_${edgeCounter++}`,
2830
+ fromNode: fromNodeId,
2831
+ fromSide: "right",
2832
+ toNode: toNodeId,
2833
+ toSide: "left",
2834
+ label: rel.relation,
2835
+ color: rel.relation === "blocks" ? "1" : "4"
2836
+ });
2837
+ }
2838
+ }
2839
+ return { nodes, edges };
2840
+ }
2841
+ getColorForDomain(domain) {
2842
+ switch (domain.toLowerCase()) {
2843
+ case "security":
2844
+ case "guard":
2845
+ return "1";
2846
+ case "compiler":
2847
+ return "2";
2848
+ case "memory":
2849
+ case "database":
2850
+ return "4";
2851
+ case "runner":
2852
+ case "bus":
2853
+ return "5";
2854
+ case "studio":
2855
+ case "frontend":
2856
+ return "6";
2857
+ default:
2858
+ return "3";
2859
+ }
2860
+ }
2861
+ parseRuleMarkdown(raw, defaultName) {
2862
+ try {
2863
+ const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
2864
+ if (!fmMatch)
2865
+ return null;
2866
+ const fmText = fmMatch[1];
2867
+ const idMatch = fmText.match(/id:\s*"([^"]+)"/);
2868
+ const nameMatch = fmText.match(/name:\s*"([^"]+)"/);
2869
+ const domainMatch = fmText.match(/domain:\s*"([^"]+)"/);
2870
+ const confMatch = fmText.match(/confidence:\s*([0-9.]+)/);
2871
+ const weightMatch = fmText.match(/weight:\s*([0-9.]+)/);
2872
+ const invariants = [];
2873
+ const invSection = fmText.match(/invariants:\r?\n((?:\s*-\s*"[^"]+"\r?\n?)+)/);
2874
+ if (invSection) {
2875
+ const itemMatches = invSection[1].matchAll(/-\s*"([^"]+)"/g);
2876
+ for (const m of itemMatches) {
2877
+ invariants.push(m[1].replaceAll("\\\"", '"'));
2878
+ }
2879
+ }
2880
+ const body = raw.slice(fmMatch[0].length).trim();
2881
+ const descMatch = body.match(/## \uD83D\uDCA1 \uC694\uC57D \uC124\uBA85\r?\n([\s\S]*?)(?:\r?\n##|$)/);
2882
+ const description = descMatch ? descMatch[1].trim() : "";
2883
+ const ruleTitle = nameMatch ? nameMatch[1] : defaultName;
2884
+ return {
2885
+ id: idMatch ? idMatch[1] : `rule_${Date.now()}`,
2886
+ title: ruleTitle,
2887
+ name: ruleTitle,
2888
+ domain: domainMatch ? domainMatch[1] : "general",
2889
+ confidence: confMatch ? parseFloat(confMatch[1]) : 1,
2890
+ weight: weightMatch ? parseFloat(weightMatch[1]) : 1,
2891
+ triggers: {
2892
+ files: [],
2893
+ keywords: [ruleTitle.toLowerCase()]
2894
+ },
2895
+ invariants: invariants || [],
2896
+ actions: [],
2897
+ context: description ? [description] : [],
2898
+ description,
2899
+ updatedAt: new Date().toISOString()
2900
+ };
2901
+ } catch {
2902
+ return null;
2903
+ }
2904
+ }
2905
+ parseTrapMarkdown(raw, defaultPattern) {
2906
+ try {
2907
+ const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
2908
+ if (!fmMatch)
2909
+ return null;
2910
+ const fmText = fmMatch[1];
2911
+ const idMatch = fmText.match(/id:\s*"([^"]+)"/);
2912
+ const titleMatch = fmText.match(/(?:title|trapPattern):\s*"([^"]+)"/);
2913
+ const domainMatch = fmText.match(/domain:\s*"([^"]+)"/);
2914
+ const sevMatch = fmText.match(/severity:\s*"([^"]+)"/);
2915
+ const body = raw.slice(fmMatch[0].length).trim();
2916
+ const reasonMatch = body.match(/## \uD83D\uDCA5 \uC2E4\uD328 \uC6D0\uC778 \(Root Cause\)\r?\n([\s\S]*?)(?:\r?\n##|$)/);
2917
+ const altMatch = body.match(/## \u2705 \uC548\uC804\uD55C \uB300\uC548 \(Safe Solution\)\r?\n([\s\S]*?)(?:\r?\n##|$)/);
2918
+ const trapTitle = titleMatch ? titleMatch[1] : defaultPattern;
2919
+ const rootCause = reasonMatch ? reasonMatch[1].trim() : "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
2920
+ const counterMeasure = altMatch ? altMatch[1].trim() : "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
2921
+ return {
2922
+ id: idMatch ? idMatch[1] : `trap_${Date.now()}`,
2923
+ title: trapTitle,
2924
+ failedApproach: trapTitle,
2925
+ trapPattern: trapTitle,
2926
+ domain: domainMatch ? domainMatch[1] : "general",
2927
+ environment: domainMatch ? domainMatch[1] : "general",
2928
+ severity: sevMatch ? sevMatch[1] : "MEDIUM",
2929
+ rootCause,
2930
+ reason: rootCause,
2931
+ counterMeasure,
2932
+ safeAlternative: counterMeasure,
2933
+ triggerKeywords: [trapTitle],
2934
+ createdAt: new Date().toISOString()
2935
+ };
2936
+ } catch {
2937
+ return null;
2938
+ }
2939
+ }
2940
+ atomicWriteFile(filePath, content) {
2941
+ const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
2942
+ writeFileSync5(tempPath, content, "utf-8");
2943
+ renameSync4(tempPath, filePath);
2944
+ }
2945
+ sanitizeFileName(name) {
2946
+ return name.replace(/\.\.+/g, "_").replace(/[\\/:*?"<>|\0]/g, "_").replace(/\s+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "unnamed";
2947
+ }
2948
+ }
2949
+
2950
+ // memory/causal-server.ts
2951
+ var CAUSAL_MCP_TOOLS = [
2952
+ {
2953
+ name: "query_causal_memory",
2954
+ description: "\uC791\uC5C5 \uCEE8\uD14D\uC2A4\uD2B8(\uC218\uC815\uB41C \uD30C\uC77C, \uD0A4\uC6CC\uB4DC, \uBA85\uB839\uC5B4, \uB3C4\uBA54\uC778)\uC5D0 \uB9E4\uCE6D\uB418\uB294 \uC778\uACFC\uC728 \uC9C0\uC2DD\uACFC \uBD88\uBCC0 \uADDC\uCE59\uB9CC 0ms \uCD08\uACBD\uB7C9\uC73C\uB85C \uACF5\uBA85 \uCFFC\uB9AC\uD569\uB2C8\uB2E4.",
2955
+ inputSchema: {
2956
+ type: "object",
2957
+ properties: {
2958
+ files: {
2959
+ type: "array",
2960
+ items: { type: "string" },
2961
+ description: "\uAC10\uC9C0\uD560 \uB300\uC0C1 \uD30C\uC77C \uACBD\uB85C \uBAA9\uB85D (\uC0DD\uB7B5 \uC2DC Git \uBCC0\uACBD \uD30C\uC77C \uC790\uB3D9 \uAC10\uC9C0)"
2962
+ },
2963
+ keywords: {
2964
+ type: "array",
2965
+ items: { type: "string" },
2966
+ description: "\uAC80\uC0C9\uD560 \uB3C4\uBA54\uC778/\uC791\uC5C5 \uD0A4\uC6CC\uB4DC \uBAA9\uB85D"
2967
+ },
2968
+ command: {
2969
+ type: "string",
2970
+ description: "\uC5F0\uB3D9 \uBA85\uB839\uC5B4 (\uC608: check, build, release, sign)"
2971
+ },
2972
+ domain: {
2973
+ type: "string",
2974
+ description: "\uD2B9\uC815 \uB3C4\uBA54\uC778 \uD544\uD130"
2975
+ },
2976
+ compact: {
2977
+ type: "boolean",
2978
+ description: "\uCD08\uC18C\uD615 JSON \uD3EC\uB9F7 \uBC18\uD658 \uC5EC\uBD80"
2979
+ }
2980
+ }
2981
+ }
2982
+ },
2983
+ {
2984
+ name: "assert_causal_rule",
2985
+ description: "\uC2E0\uADDC \uC778\uACFC\uC728 \uADDC\uCE59(\uC6D0\uC778 \uD2B8\uB9AC\uAC70 \u2794 \uBD88\uBCC0 \uACC4\uC57D \u2794 \uC2E4\uD589 \uC9C0\uCE68)\uC744 \uBA54\uBAA8\uB9AC\uC5D0 \uB4F1\uB85D\uD558\uAC70\uB098 \uAC31\uC2E0\uD569\uB2C8\uB2E4.",
2986
+ inputSchema: {
2987
+ type: "object",
2988
+ required: ["id", "domain", "title", "triggers"],
2989
+ properties: {
2990
+ id: { type: "string" },
2991
+ domain: { type: "string" },
2992
+ title: { type: "string" },
2993
+ triggers: { type: "object" },
2994
+ invariants: { type: "array", items: { type: "string" } },
2995
+ actions: { type: "array", items: { type: "string" } },
2996
+ context: { type: "array", items: { type: "string" } }
2997
+ }
2998
+ }
2999
+ },
3000
+ {
3001
+ name: "audit_causal_memory",
3002
+ description: "\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC758 \uBB34\uACB0\uC131, \uB3C4\uBA54\uC778 \uBD84\uD3EC, \uD1A0\uD070 \uC808\uAC10 \uC0C1\uD0DC\uB97C \uAC10\uC0AC(Audit)\uD569\uB2C8\uB2E4.",
3003
+ inputSchema: {
3004
+ type: "object",
3005
+ properties: {}
3006
+ }
3007
+ },
3008
+ {
3009
+ name: "read_graph",
3010
+ description: "Read the active causal resonance knowledge graph (automatically filters out unrelated noise for 90%+ token savings)",
3011
+ inputSchema: {
3012
+ type: "object",
3013
+ properties: {}
3014
+ }
3015
+ },
3016
+ {
3017
+ name: "search_nodes",
3018
+ description: "Search for causal rules and nodes in the knowledge graph based on a query and traverse causal relations",
3019
+ inputSchema: {
3020
+ type: "object",
3021
+ required: ["query"],
3022
+ properties: {
3023
+ query: {
3024
+ type: "string",
3025
+ description: "The search query to match against rule titles, domains, triggers, and invariants"
3026
+ }
3027
+ }
3028
+ }
3029
+ },
3030
+ {
3031
+ name: "open_nodes",
3032
+ description: "Open specific causal rule nodes in the knowledge graph by their names or IDs",
3033
+ inputSchema: {
3034
+ type: "object",
3035
+ required: ["names"],
3036
+ properties: {
3037
+ names: {
3038
+ type: "array",
3039
+ items: { type: "string" },
3040
+ description: "An array of entity names or rule IDs to retrieve"
3041
+ }
3042
+ }
3043
+ }
3044
+ },
3045
+ {
3046
+ name: "create_entities",
3047
+ description: "Create multiple new entities (automatically converted into structured Causal Rules)",
3048
+ inputSchema: {
3049
+ type: "object",
3050
+ required: ["entities"],
3051
+ properties: {
3052
+ entities: {
3053
+ type: "array",
3054
+ items: {
3055
+ type: "object",
3056
+ required: ["name", "entityType", "observations"],
3057
+ properties: {
3058
+ name: { type: "string" },
3059
+ entityType: { type: "string" },
3060
+ observations: { type: "array", items: { type: "string" } }
3061
+ }
3062
+ }
3063
+ }
3064
+ }
3065
+ }
3066
+ },
3067
+ {
3068
+ name: "create_relations",
3069
+ description: "Create multiple new causal relations between nodes in the knowledge graph",
3070
+ inputSchema: {
3071
+ type: "object",
3072
+ required: ["relations"],
3073
+ properties: {
3074
+ relations: {
3075
+ type: "array",
3076
+ items: {
3077
+ type: "object",
3078
+ required: ["from", "to", "relationType"],
3079
+ properties: {
3080
+ from: { type: "string" },
3081
+ to: { type: "string" },
3082
+ relationType: { type: "string" }
3083
+ }
3084
+ }
3085
+ }
3086
+ }
3087
+ }
3088
+ },
3089
+ {
3090
+ name: "add_observations",
3091
+ description: "Add new invariants, actions, or context observations to existing causal rules",
3092
+ inputSchema: {
3093
+ type: "object",
3094
+ required: ["observations"],
3095
+ properties: {
3096
+ observations: {
3097
+ type: "array",
3098
+ items: {
3099
+ type: "object",
3100
+ required: ["entityName", "contents"],
3101
+ properties: {
3102
+ entityName: { type: "string" },
3103
+ contents: { type: "array", items: { type: "string" } }
3104
+ }
3105
+ }
3106
+ }
3107
+ }
3108
+ }
3109
+ },
3110
+ {
3111
+ name: "delete_entities",
3112
+ description: "Delete multiple entities/rules and their associated causal relations",
3113
+ inputSchema: {
3114
+ type: "object",
3115
+ required: ["entityNames"],
3116
+ properties: {
3117
+ entityNames: {
3118
+ type: "array",
3119
+ items: { type: "string" }
3120
+ }
3121
+ }
3122
+ }
3123
+ },
3124
+ {
3125
+ name: "delete_observations",
3126
+ description: "Delete specific observations from entities/rules in the causal memory",
3127
+ inputSchema: {
3128
+ type: "object",
3129
+ required: ["deletions"],
3130
+ properties: {
3131
+ deletions: {
3132
+ type: "array",
3133
+ items: {
3134
+ type: "object",
3135
+ required: ["entityName", "observations"],
3136
+ properties: {
3137
+ entityName: { type: "string" },
3138
+ observations: { type: "array", items: { type: "string" } }
3139
+ }
3140
+ }
3141
+ }
3142
+ }
3143
+ }
3144
+ },
3145
+ {
3146
+ name: "delete_relations",
3147
+ description: "Delete multiple causal relations from the knowledge graph",
3148
+ inputSchema: {
3149
+ type: "object",
3150
+ required: ["relations"],
3151
+ properties: {
3152
+ relations: {
3153
+ type: "array",
3154
+ items: {
3155
+ type: "object",
3156
+ required: ["from", "to", "relationType"],
3157
+ properties: {
3158
+ from: { type: "string" },
3159
+ to: { type: "string" },
3160
+ relationType: { type: "string" }
3161
+ }
3162
+ }
3163
+ }
3164
+ }
3165
+ }
3166
+ },
3167
+ {
3168
+ name: "save_context_checkpoint",
3169
+ description: "\uD604\uC7AC \uC791\uC5C5 \uC138\uC158\uC758 \uD575\uC2EC \uCEE8\uD14D\uC2A4\uD2B8(\uBAA9\uD45C, \uC644\uB8CC \uB2E8\uACC4, \uB2E4\uC74C \uD560 \uC77C, \uD575\uC2EC \uC124\uACC4 \uACB0\uC815, \uD65C\uC131 \uD30C\uC77C, \uC544\uD2F0\uD329\uD2B8)\uB97C \uCD08\uACBD\uB7C9 \uC2A4\uB0C5\uC0F7\uC73C\uB85C \uC800\uC7A5\uD558\uC5EC \uC0C8 \uCC44\uD305\uCC3D\uC5D0\uC11C\uB3C4 \uAE30\uC5B5 \uC190\uC2E4 \uC5C6\uC774 0ms \uBCF5\uC6D0\uD560 \uC218 \uC788\uAC8C \uD569\uB2C8\uB2E4.",
3170
+ inputSchema: {
3171
+ type: "object",
3172
+ required: ["title"],
3173
+ properties: {
3174
+ id: { type: "string", description: "\uACE0\uC720 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (\uC0DD\uB7B5 \uC2DC \uC790\uB3D9 \uC0DD\uC131)" },
3175
+ sessionId: { type: "string", description: "\uD504\uB85C\uC81D\uD2B8 \uB610\uB294 \uC138\uC158 \uC2DD\uBCC4\uC790 (\uC608: synapse-kit)" },
3176
+ title: { type: "string", description: "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uC694\uC57D \uC81C\uBAA9" },
3177
+ goal: { type: "string", description: "\uD604\uC7AC \uB2EC\uC131 \uC911\uC778 \uD575\uC2EC \uBAA9\uD45C" },
3178
+ completedSteps: {
3179
+ type: "array",
3180
+ items: { type: "string" },
3181
+ description: "\uC774\uBC88 \uC138\uC158\uAE4C\uC9C0 \uC644\uB8CC\uB41C \uD575\uC2EC \uC791\uC5C5 \uBAA9\uB85D"
3182
+ },
3183
+ pendingTasks: {
3184
+ type: "array",
3185
+ items: { type: "string" },
3186
+ description: "\uB2E4\uC74C \uCC44\uD305/\uC138\uC158\uC5D0\uC11C \uBC14\uB85C \uC774\uC5B4\uC11C \uC9C4\uD589\uD560 \uD560 \uC77C \uBAA9\uB85D"
3187
+ },
3188
+ keyDecisions: {
3189
+ type: "array",
3190
+ items: { type: "string" },
3191
+ description: "\uD575\uC2EC \uC124\uACC4 \uACB0\uC815 \uBC0F \uC544\uD0A4\uD14D\uCC98 \uC8FC\uC758\uC0AC\uD56D"
3192
+ },
3193
+ activeFiles: {
3194
+ type: "array",
3195
+ items: { type: "string" },
3196
+ description: "\uC9D1\uC911 \uC791\uC5C5 \uC911\uC778 \uD30C\uC77C \uACBD\uB85C \uBAA9\uB85D"
3197
+ },
3198
+ artifacts: {
3199
+ type: "array",
3200
+ items: { type: "string" },
3201
+ description: "\uAD00\uB828 \uC544\uD2F0\uD329\uD2B8 \uB610\uB294 \uB9C8\uC2A4\uD130 \uC124\uACC4 \uBB38\uC11C \uACBD\uB85C \uBAA9\uB85D"
3202
+ },
3203
+ cwd: { type: "string", description: "\uC791\uC5C5 \uB514\uB809\uD130\uB9AC \uACBD\uB85C" }
3204
+ }
3205
+ }
3206
+ },
3207
+ {
3208
+ name: "load_context_checkpoint",
3209
+ description: "\uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC2A4\uB0C5\uC0F7(\uCD5C\uC2E0 \uB610\uB294 \uD2B9\uC815 ID/\uC138\uC158)\uC744 0ms \uB9CC\uC5D0 \uBCF5\uC6D0\uD558\uC5EC \uC0C8 \uCC44\uD305\uCC3D\uC5D0\uC11C \uC774\uC804 \uC791\uC5C5\uC744 \uC989\uC2DC \uC774\uC5B4\uAC11\uB2C8\uB2E4.",
3210
+ inputSchema: {
3211
+ type: "object",
3212
+ properties: {
3213
+ id: {
3214
+ type: "string",
3215
+ description: "\uBCF5\uC6D0\uD560 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID, \uC81C\uBAA9 \uD0A4\uC6CC\uB4DC, \uB610\uB294 'latest' (\uAE30\uBCF8\uAC12: 'latest')"
3216
+ },
3217
+ sessionId: {
3218
+ type: "string",
3219
+ description: "\uD2B9\uC815 \uC138\uC158/\uD504\uB85C\uC81D\uD2B8\uC758 \uCD5C\uC2E0 \uC2A4\uB0C5\uC0F7\uB9CC \uD544\uD130\uB9C1\uD560 \uACBD\uC6B0 \uC9C0\uC815"
3220
+ },
3221
+ compact: {
3222
+ type: "boolean",
3223
+ description: "\uCD08\uC18C\uD615 JSON \uD3EC\uB9F7\uC73C\uB85C \uBC18\uD658\uD560\uC9C0 \uC5EC\uBD80"
3224
+ }
3225
+ }
3226
+ }
3227
+ },
3228
+ {
3229
+ name: "list_context_checkpoints",
3230
+ description: "\uC800\uC7A5\uB41C \uBAA8\uB4E0 \uC791\uC5C5 \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7(Checkpoint) \uBAA9\uB85D\uC744 \uCD5C\uC2E0\uC21C\uC73C\uB85C \uC870\uD68C\uD569\uB2C8\uB2E4.",
3231
+ inputSchema: {
3232
+ type: "object",
3233
+ properties: {
3234
+ sessionId: { type: "string", description: "\uD2B9\uC815 \uC138\uC158/\uD504\uB85C\uC81D\uD2B8 \uD544\uD130" },
3235
+ limit: { type: "number", description: "\uC870\uD68C\uD560 \uCD5C\uB300 \uAC1C\uC218 (\uAE30\uBCF8\uAC12: 20)" }
3236
+ }
3237
+ }
3238
+ },
3239
+ {
3240
+ name: "time_travel_causal_memory",
3241
+ description: "\uC778\uACFC\uC728 \uC2DC\uACC4\uC5F4(Time-Series) \uC5D4\uC9C4\uC744 \uD1B5\uD574 \uC2DC\uAC04 \uAC10\uC1E0 \uAC00\uC911\uCE58 \uACC4\uC0B0, \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uAC04 \uBCC0\uACBD diff \uBD84\uC11D, \uD2B9\uC815 \uACFC\uAC70 \uC2DC\uC810\uC73C\uB85C\uC758 \uB9AC\uC640\uC778\uB4DC, \uC138\uC158 \uD0C0\uC784\uB77C\uC778\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4.",
3242
+ inputSchema: {
3243
+ type: "object",
3244
+ required: ["action"],
3245
+ properties: {
3246
+ action: {
3247
+ type: "string",
3248
+ enum: ["diff", "rewind", "timeline", "decay"],
3249
+ description: "\uC218\uD589\uD560 \uC2DC\uACC4\uC5F4 \uB3D9\uC791 (diff: \uBE44\uAD50, rewind: \uB418\uB3CC\uB9AC\uAE30, timeline: \uD0C0\uC784\uB77C\uC778, decay: \uAC10\uC1E0 \uACC4\uC0B0)"
3250
+ },
3251
+ fromId: { type: "string", description: "\uBE44\uAD50 \uAE30\uC900 \uC2DC\uC791 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (diff \uC804\uC6A9)" },
3252
+ toId: { type: "string", description: "\uBE44\uAD50 \uB300\uC0C1 \uBAA9\uD45C \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (diff \uC804\uC6A9)" },
3253
+ targetId: { type: "string", description: "\uB9AC\uC640\uC778\uB4DC \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (rewind \uC804\uC6A9)" },
3254
+ limit: { type: "number", description: "\uD0C0\uC784\uB77C\uC778 \uC870\uD68C \uAC1C\uC218" }
3255
+ }
3256
+ }
3257
+ },
3258
+ {
3259
+ name: "scan_causal_graveyard",
3260
+ description: "\uBD80\uC815\uC801 \uAE30\uC5B5 / \uC548\uD2F0\uD328\uD134 \uD568\uC815(Graveyard)\uC744 \uC2E4\uC2DC\uAC04\uC73C\uB85C \uC2A4\uCE94\uD558\uC5EC \uC2E4\uD328\uB97C \uC720\uBC1C\uD588\uB358 \uC811\uADFC\uBC95\uC778\uC9C0 \uC120\uC81C \uAC80\uC99D\uD558\uAC70\uB098, \uC0C8\uB85C\uC6B4 \uC2E4\uD328 \uAC00\uC124\uC744 \uB4F1\uB85D\uD569\uB2C8\uB2E4.",
3261
+ inputSchema: {
3262
+ type: "object",
3263
+ required: ["action"],
3264
+ properties: {
3265
+ action: {
3266
+ type: "string",
3267
+ enum: ["scan", "assert", "list"],
3268
+ description: "\uB3D9\uC791 (scan: \uD568\uC815 \uAC80\uC0AC, assert: \uC2E4\uD328 \uAC00\uC124 \uB4F1\uB85D, list: \uC804\uCCB4 \uBAA9\uB85D)"
3269
+ },
3270
+ files: { type: "array", items: { type: "string" }, description: "\uC218\uC815 \uC608\uC815 \uD30C\uC77C \uBAA9\uB85D (scan\uC6A9)" },
3271
+ keywords: { type: "array", items: { type: "string" }, description: "\uC758\uB3C4/\uC811\uADFC\uBC95 \uD0A4\uC6CC\uB4DC (scan\uC6A9)" },
3272
+ trapPattern: { type: "string", description: "\uC2E4\uD328 \uC720\uBC1C \uD328\uD134 \uC774\uB984 (assert\uC6A9)" },
3273
+ domain: { type: "string", description: "\uB3C4\uBA54\uC778 (assert\uC6A9)" },
3274
+ reason: { type: "string", description: "\uC2E4\uD328 \uC6D0\uC778 (assert\uC6A9)" },
3275
+ safeAlternative: { type: "string", description: "\uC548\uC804\uD55C \uB300\uC548 (assert\uC6A9)" },
3276
+ severity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"], description: "\uC704\uD5D8\uB3C4" },
3277
+ avoidFiles: { type: "array", items: { type: "string" }, description: "\uAE08\uC9C0 \uD30C\uC77C \uD328\uD134" }
3278
+ }
3279
+ }
3280
+ },
3281
+ {
3282
+ name: "branch_causal_context",
3283
+ description: "\uC791\uC5C5 \uB9E5\uB77D\uC744 \uB3C5\uB9BD\uB41C \uBE0C\uB79C\uCE58(Mental Fork)\uB85C \uBD84\uAE30\uD558\uAC70\uB098, \uAC80\uC99D \uD6C4 \uBA54\uC778\uC5D0 \uBCD1\uD569(Merge)\uD558\uACE0, \uC2E4\uD328\uD55C \uC2E4\uD5D8\uC740 \uD3D0\uAE30(Abandon)\uD558\uC5EC Graveyard\uC5D0 \uC548\uD2F0\uD328\uD134\uC73C\uB85C \uC790\uB3D9 \uC774\uAD00\uD569\uB2C8\uB2E4.",
3284
+ inputSchema: {
3285
+ type: "object",
3286
+ required: ["action"],
3287
+ properties: {
3288
+ action: {
3289
+ type: "string",
3290
+ enum: ["list", "create", "switch", "merge", "abandon"],
3291
+ description: "\uBE0C\uB79C\uCE58 \uB3D9\uC791 (list: \uBAA9\uB85D, create: \uC0DD\uC131, switch: \uC804\uD658, merge: \uBCD1\uD569, abandon: \uD3D0\uAE30 \uBC0F \uD568\uC815 \uB4F1\uB85D)"
3292
+ },
3293
+ name: { type: "string", description: "\uBE0C\uB79C\uCE58 \uC774\uB984" },
3294
+ fromBranch: { type: "string", description: "\uBD84\uAE30 \uAE30\uC900 \uBD80\uBAA8 \uBE0C\uB79C\uCE58 (create\uC6A9)" },
3295
+ targetBranch: { type: "string", description: "\uBCD1\uD569 \uB300\uC0C1 \uBE0C\uB79C\uCE58 (merge\uC6A9)" },
3296
+ failureReason: { type: "string", description: "\uD3D0\uAE30 \uC0AC\uC720 (abandon\uC6A9)" },
3297
+ safeAlternative: { type: "string", description: "\uC548\uC804\uD55C \uB300\uC548 (abandon\uC6A9)" }
3298
+ }
3299
+ }
3300
+ },
3301
+ {
3302
+ name: "predict_blast_radius",
3303
+ description: "\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C\uB4E4\uC744 \uAE30\uBC18\uC73C\uB85C \uC778\uACFC \uADF8\uB798\uD504\uC758 2~3\uB2E8\uACC4 \uC5F0\uC1C4 \uD30C\uAE09 \uC601\uD5A5(Blast Radius)\uACFC \uC704\uD5D8\uB3C4(0~100)\uB97C \uC0B0\uCD9C\uD558\uACE0, \uC704\uBC18 \uBC29\uC9C0\uB97C \uC704\uD574 \uAD00\uB828 \uBD88\uBCC0\uC2DD\uB4E4\uC744 \uC0AC\uC804 \uD398\uCE58(prefetch)\uD569\uB2C8\uB2E4.",
3304
+ inputSchema: {
3305
+ type: "object",
3306
+ required: ["files"],
3307
+ properties: {
3308
+ files: {
3309
+ type: "array",
3310
+ items: { type: "string" },
3311
+ description: "\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C \uBAA9\uB85D"
3312
+ },
3313
+ maxHops: {
3314
+ type: "number",
3315
+ description: "\uCD5C\uB300 \uD30C\uAE09 \uC804\uD30C \uB2E8\uACC4 (\uAE30\uBCF8\uAC12: 3)"
3316
+ }
3317
+ }
3318
+ }
3319
+ },
3320
+ {
3321
+ name: "synthesize_git_checkpoint",
3322
+ description: "Git \uC0C1\uD0DC \uBC0F diff\uB97C \uBD84\uC11D\uD558\uC5EC \uC791\uC5C5\uC790\uC758 \uC758\uB3C4(Goal, Completed Steps, Pending Tasks)\uB97C \uC790\uB3D9 \uC5ED\uD569\uC131(Reverse Synthesis)\uD558\uACE0 \uC6D0\uD074\uB9AD \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4.",
3323
+ inputSchema: {
3324
+ type: "object",
3325
+ properties: {
3326
+ cwd: { type: "string", description: "\uC791\uC5C5 \uB514\uB809\uD130\uB9AC \uACBD\uB85C" },
3327
+ title: { type: "string", description: "\uC0AC\uC6A9\uC790 \uC9C0\uC815 \uC81C\uBAA9 (\uC0DD\uB7B5 \uC2DC \uC790\uB3D9 \uC0DD\uC131)" },
3328
+ autoCheckpoint: { type: "boolean", description: "\uBD84\uC11D \uD6C4 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC989\uC2DC \uC800\uC7A5 \uC5EC\uBD80 (\uAE30\uBCF8: true)" }
3329
+ }
3330
+ }
3331
+ },
3332
+ {
3333
+ name: "sync_obsidian_lens",
3334
+ description: "\uC635\uC2DC\uB514\uC5B8(Obsidian) \uBCFC\uD2B8\uC640 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC(\uADDC\uCE59, \uD568\uC815, \uC2A4\uB0C5\uC0F7, \uD0C0\uC784\uB77C\uC778, \uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8)\uB97C \uC644\uBCBD\uD558\uAC8C \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654(Sync)\uD569\uB2C8\uB2E4.",
3335
+ inputSchema: {
3336
+ type: "object",
3337
+ properties: {
3338
+ action: {
3339
+ type: "string",
3340
+ enum: ["export", "import", "sync"],
3341
+ description: "\uB3D9\uC791 (export: \uB9C8\uD06C\uB2E4\uC6B4/\uCE94\uBC84\uC2A4 \uB0B4\uBCF4\uB0B4\uAE30, import: \uB9C8\uD06C\uB2E4\uC6B4 \uC77D\uC5B4\uC624\uAE30, sync: \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654)"
3342
+ },
3343
+ vaultPath: {
3344
+ type: "string",
3345
+ description: "\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uB8E8\uD2B8 \uACBD\uB85C (\uC0DD\uB7B5 \uC2DC \uAE30\uBCF8 \uD658\uACBD\uBCC0\uC218/OS \uACBD\uB85C \uC0AC\uC6A9)"
3346
+ }
3347
+ }
3348
+ }
3349
+ }
3350
+ ];
3351
+ async function handleMcpJsonRpcRequest(rpc, store, dbPath, contextDbPath) {
3352
+ const { id, method, params } = rpc;
3353
+ const contextStore = new ContextCheckpointStore(contextDbPath, dbPath);
3354
+ if (method.startsWith("notifications/")) {
3355
+ return null;
3356
+ }
3357
+ if (method === "initialize") {
3358
+ return {
3359
+ jsonrpc: "2.0",
3360
+ id: id ?? null,
3361
+ result: {
3362
+ protocolVersion: params?.protocolVersion || CAUSAL_MEMORY_CONFIG.MCP_PROTOCOL_VERSION,
3363
+ capabilities: {
3364
+ tools: {}
3365
+ },
3366
+ serverInfo: {
3367
+ name: CAUSAL_MEMORY_CONFIG.MCP_SERVER_NAME,
3368
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION
3369
+ }
3370
+ }
3371
+ };
3372
+ }
3373
+ if (method === "ping") {
3374
+ return {
3375
+ jsonrpc: "2.0",
3376
+ id: id ?? null,
3377
+ result: {}
3378
+ };
3379
+ }
3380
+ if (method === "tools/list") {
3381
+ return {
3382
+ jsonrpc: "2.0",
3383
+ id: id ?? null,
3384
+ result: {
3385
+ tools: CAUSAL_MCP_TOOLS
3386
+ }
3387
+ };
3388
+ }
3389
+ if (method === "tools/call") {
3390
+ store.reloadIfNeeded();
3391
+ contextStore.reloadIfNeeded();
3392
+ const toolName = params?.name;
3393
+ const args = params?.arguments || {};
3394
+ try {
3395
+ let payloadText = "";
3396
+ switch (toolName) {
3397
+ case "query_causal_memory": {
3398
+ let files = Array.isArray(args.files) ? args.files : [];
3399
+ if (files.length === 0 && args.autoGit !== false) {
3400
+ files = await detectModifiedFiles();
3401
+ }
3402
+ const res = store.queryResonance({
3403
+ files,
3404
+ keywords: args.keywords,
3405
+ command: args.command,
3406
+ domain: args.domain,
3407
+ propagateRelations: args.propagateRelations,
3408
+ maxHops: args.maxHops
3409
+ });
3410
+ payloadText = formatResonanceForAgent(res, Boolean(args.compact));
3411
+ const latestCtx = contextStore.getLatestCheckpoint(typeof args.sessionId === "string" ? args.sessionId : undefined);
3412
+ if (latestCtx && !args.compact && args.includeContext !== false) {
3413
+ payloadText += `
3414
+
3415
+ ` + ContextCheckpointStore.formatForAgentResume(latestCtx, false);
3416
+ }
3417
+ break;
3418
+ }
3419
+ case "assert_causal_rule": {
3420
+ store.assertRule(args);
3421
+ payloadText = JSON.stringify({
3422
+ ok: true,
3423
+ message: `\uC778\uACFC\uC728 \uADDC\uCE59 \uB4F1\uB85D/\uAC31\uC2E0 \uC644\uB8CC: ${args.id}`,
3424
+ id: args.id
3425
+ });
3426
+ break;
3427
+ }
3428
+ case "audit_causal_memory": {
3429
+ const audit = store.audit();
3430
+ const status = store.getStatus();
3431
+ const checkpointsCount = contextStore.listCheckpoints().length;
3432
+ payloadText = JSON.stringify({ ...audit, status, checkpointsCount }, null, 2);
3433
+ break;
3434
+ }
3435
+ case "read_graph": {
3436
+ const gitFiles = await detectModifiedFiles();
3437
+ const smartGraph = store.readSmartGraph(gitFiles);
3438
+ const latestCheckpoint = contextStore.getLatestCheckpoint();
3439
+ payloadText = JSON.stringify({
3440
+ ...smartGraph,
3441
+ latestContextCheckpoint: latestCheckpoint || null
3442
+ }, null, 2);
3443
+ break;
3444
+ }
3445
+ case "save_context_checkpoint": {
3446
+ const saved = await contextStore.saveCheckpoint(args);
3447
+ payloadText = JSON.stringify({
3448
+ ok: true,
3449
+ message: `\uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC800\uC7A5 \uC644\uB8CC: ${saved.id}`,
3450
+ checkpoint: saved
3451
+ }, null, 2);
3452
+ break;
3453
+ }
3454
+ case "load_context_checkpoint": {
3455
+ const targetId = typeof args.id === "string" ? args.id : "latest";
3456
+ const sessionId = typeof args.sessionId === "string" ? args.sessionId : undefined;
3457
+ const found = contextStore.getCheckpoint(targetId, sessionId);
3458
+ if (!found) {
3459
+ payloadText = JSON.stringify({
3460
+ ok: false,
3461
+ message: "\uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
3462
+ });
3463
+ } else {
3464
+ payloadText = ContextCheckpointStore.formatForAgentResume(found, Boolean(args.compact));
3465
+ }
3466
+ break;
3467
+ }
3468
+ case "list_context_checkpoints": {
3469
+ const sessionId = typeof args.sessionId === "string" ? args.sessionId : undefined;
3470
+ const limit = typeof args.limit === "number" ? args.limit : 20;
3471
+ const list = contextStore.listCheckpoints({ sessionId, limit });
3472
+ payloadText = JSON.stringify({
3473
+ ok: true,
3474
+ totalCount: list.length,
3475
+ checkpoints: list.map((c) => ({
3476
+ id: c.id,
3477
+ sessionId: c.sessionId,
3478
+ title: c.title,
3479
+ createdAt: c.createdAt,
3480
+ goal: c.goal,
3481
+ pendingTasksCount: c.pendingTasks.length,
3482
+ completedStepsCount: c.completedSteps.length
3483
+ }))
3484
+ }, null, 2);
3485
+ break;
3486
+ }
3487
+ case "search_nodes": {
3488
+ const searchResult = store.searchNodes(String(args.query || ""));
3489
+ payloadText = JSON.stringify(searchResult, null, 2);
3490
+ break;
3491
+ }
3492
+ case "open_nodes": {
3493
+ const names = Array.isArray(args.names) ? args.names : [];
3494
+ const openResult = store.openNodes(names);
3495
+ payloadText = JSON.stringify(openResult, null, 2);
3496
+ break;
3497
+ }
3498
+ case "create_entities": {
3499
+ const entities = Array.isArray(args.entities) ? args.entities : [];
3500
+ const created = store.createEntities(entities);
3501
+ payloadText = JSON.stringify(created, null, 2);
3502
+ break;
3503
+ }
3504
+ case "create_relations": {
3505
+ const relations = Array.isArray(args.relations) ? args.relations : [];
3506
+ const created = store.createRelations(relations);
3507
+ payloadText = JSON.stringify(created, null, 2);
3508
+ break;
3509
+ }
3510
+ case "add_observations": {
3511
+ const observations = Array.isArray(args.observations) ? args.observations : [];
3512
+ const added = store.addObservations(observations);
3513
+ payloadText = JSON.stringify(added, null, 2);
3514
+ break;
3515
+ }
3516
+ case "delete_entities": {
3517
+ const entityNames = Array.isArray(args.entityNames) ? args.entityNames : [];
3518
+ store.deleteEntities(entityNames);
3519
+ payloadText = "Entities deleted successfully";
3520
+ break;
3521
+ }
3522
+ case "delete_observations": {
3523
+ const deletions = Array.isArray(args.deletions) ? args.deletions : [];
3524
+ store.deleteObservations(deletions);
3525
+ payloadText = "Observations deleted successfully";
3526
+ break;
3527
+ }
3528
+ case "delete_relations": {
3529
+ const relations = Array.isArray(args.relations) ? args.relations : [];
3530
+ store.deleteRelations(relations);
3531
+ payloadText = "Relations deleted successfully";
3532
+ break;
3533
+ }
3534
+ case "time_travel_causal_memory": {
3535
+ const timeSeries = new CausalTimeSeriesEngine(contextStore, store);
3536
+ if (args.action === "diff") {
3537
+ const diff = timeSeries.diffCheckpoints(String(args.fromId || ""), String(args.toId || ""));
3538
+ payloadText = JSON.stringify(diff, null, 2);
3539
+ } else if (args.action === "rewind") {
3540
+ const res = await timeSeries.rewindToCheckpoint(String(args.targetId || ""));
3541
+ payloadText = JSON.stringify(res, null, 2);
3542
+ } else if (args.action === "timeline") {
3543
+ const tl = timeSeries.generateTimeline(typeof args.limit === "number" ? args.limit : 20);
3544
+ payloadText = JSON.stringify(tl, null, 2);
3545
+ } else {
3546
+ const resonance = store.queryResonance({
3547
+ files: Array.isArray(args.files) ? args.files : []
3548
+ });
3549
+ const decayed = timeSeries.computeDecayedResonance(resonance.resonanceRules);
3550
+ payloadText = JSON.stringify(decayed, null, 2);
3551
+ }
3552
+ break;
3553
+ }
3554
+ case "scan_causal_graveyard": {
3555
+ const graveyard = new CausalGraveyardStore(store);
3556
+ if (args.action === "assert") {
3557
+ const trap = graveyard.assertTrap(args);
3558
+ payloadText = JSON.stringify({ ok: true, trap }, null, 2);
3559
+ } else if (args.action === "list") {
3560
+ const list = graveyard.getAllTraps();
3561
+ payloadText = JSON.stringify({ ok: true, traps: list }, null, 2);
3562
+ } else {
3563
+ const res = graveyard.scanTraps({
3564
+ files: Array.isArray(args.files) ? args.files : undefined,
3565
+ keywords: Array.isArray(args.keywords) ? args.keywords : undefined
3566
+ });
3567
+ payloadText = CausalGraveyardStore.formatTrapWarnings(res);
3568
+ }
3569
+ break;
3570
+ }
3571
+ case "branch_causal_context": {
3572
+ const branchStore = new CausalBranchStore(contextStore);
3573
+ if (args.action === "create") {
3574
+ const b = await branchStore.createBranch(String(args.name || ""), {
3575
+ description: args.fromBranch
3576
+ });
3577
+ payloadText = JSON.stringify({ ok: true, branch: b }, null, 2);
3578
+ } else if (args.action === "switch") {
3579
+ const b = branchStore.switchBranch(String(args.name || ""));
3580
+ payloadText = JSON.stringify({ ok: true, branch: b }, null, 2);
3581
+ } else if (args.action === "merge") {
3582
+ const res = await branchStore.mergeBranch(String(args.name || ""), args.targetBranch);
3583
+ payloadText = JSON.stringify(res, null, 2);
3584
+ } else if (args.action === "abandon") {
3585
+ const res = branchStore.abandonBranch(String(args.name || ""), String(args.failureReason || "\uD3D0\uAE30\uB428"), args.safeAlternative);
3586
+ payloadText = JSON.stringify(res, null, 2);
3587
+ } else {
3588
+ const branches = branchStore.listBranches();
3589
+ const current = branchStore.getCurrentBranch();
3590
+ payloadText = JSON.stringify({ ok: true, currentBranch: current, branches }, null, 2);
3591
+ }
3592
+ break;
3593
+ }
3594
+ case "predict_blast_radius": {
3595
+ const predictor = new CausalPredictorEngine(store, contextStore);
3596
+ const files = Array.isArray(args.files) ? args.files : [];
3597
+ const maxHops = typeof args.maxHops === "number" ? args.maxHops : 3;
3598
+ const report = predictor.analyzeBlastRadius(files, maxHops);
3599
+ payloadText = JSON.stringify(report, null, 2);
3600
+ break;
3601
+ }
3602
+ case "synthesize_git_checkpoint": {
3603
+ const synthesizer = new CausalSynthesizerEngine(contextStore, store);
3604
+ const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
3605
+ if (args.autoCheckpoint === false) {
3606
+ const summary = await synthesizer.inspectGitChanges(cwd);
3607
+ const intent = synthesizer.synthesizeIntent(summary, args.title);
3608
+ payloadText = JSON.stringify({ summary, intent }, null, 2);
3609
+ } else {
3610
+ const cp = await synthesizer.autoCheckpoint(cwd, { title: args.title });
3611
+ payloadText = JSON.stringify({ ok: true, checkpoint: cp }, null, 2);
3612
+ }
3613
+ break;
3614
+ }
3615
+ case "sync_obsidian_lens": {
3616
+ const lens = new CausalObsidianLensEngine(store, contextStore);
3617
+ const vaultPath = typeof args.vaultPath === "string" ? args.vaultPath : undefined;
3618
+ if (args.action === "export") {
3619
+ const exp = await lens.exportToVault(vaultPath);
3620
+ payloadText = JSON.stringify(exp, null, 2);
3621
+ } else if (args.action === "import") {
3622
+ const imp = await lens.importFromVault(vaultPath);
3623
+ payloadText = JSON.stringify(imp, null, 2);
3624
+ } else {
3625
+ const syncRes = await lens.syncWithVault(vaultPath);
3626
+ payloadText = JSON.stringify(syncRes, null, 2);
3627
+ }
3628
+ break;
3629
+ }
3630
+ default:
3631
+ return {
3632
+ jsonrpc: "2.0",
3633
+ id: id ?? null,
3634
+ error: {
3635
+ code: -32601,
3636
+ message: `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB3C4\uAD6C\uC785\uB2C8\uB2E4: ${toolName}`
3637
+ }
3638
+ };
3639
+ }
3640
+ return {
3641
+ jsonrpc: "2.0",
3642
+ id: id ?? null,
3643
+ result: {
3644
+ content: [{ type: "text", text: payloadText }]
3645
+ }
3646
+ };
3647
+ } catch (error) {
3648
+ return {
3649
+ jsonrpc: "2.0",
3650
+ id: id ?? null,
3651
+ result: {
3652
+ content: [
3653
+ {
3654
+ type: "text",
3655
+ text: `\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB3C4\uAD6C \uC2E4\uD589 \uC624\uB958: ${error instanceof Error ? error.message : String(error)}`
3656
+ }
3657
+ ],
3658
+ isError: true
3659
+ }
3660
+ };
3661
+ }
3662
+ }
3663
+ return {
3664
+ jsonrpc: "2.0",
3665
+ id: id ?? null,
3666
+ error: { code: -32601, message: `Method not found: ${method}` }
3667
+ };
3668
+ }
3669
+ function startCausalServer(options = {}) {
3670
+ const port = options.port || CAUSAL_MEMORY_CONFIG.DEFAULT_PORT;
3671
+ const host = options.host || CAUSAL_MEMORY_CONFIG.DEFAULT_HOST;
3672
+ const store = new CausalMemoryStore(options.dbPath);
3673
+ const contextStore = new ContextCheckpointStore(options.contextDbPath, options.dbPath);
3674
+ const server = Bun.serve({
3675
+ port,
3676
+ hostname: host,
3677
+ async fetch(req) {
3678
+ const url = new URL(req.url);
3679
+ const headers = {
3680
+ "Content-Type": "application/json",
3681
+ "Access-Control-Allow-Origin": "*",
3682
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
3683
+ "Access-Control-Allow-Headers": "Content-Type"
3684
+ };
3685
+ if (req.method === "OPTIONS") {
3686
+ return new Response(null, { headers });
3687
+ }
3688
+ try {
3689
+ if (url.pathname === "/health" || url.pathname === "/status") {
3690
+ store.reloadIfNeeded();
3691
+ return new Response(JSON.stringify({ ok: true, status: store.getStatus() }), { headers });
3692
+ }
3693
+ if (url.pathname === "/query" && req.method === "GET") {
3694
+ store.reloadIfNeeded();
3695
+ const files = url.searchParams.getAll("files");
3696
+ const keywords = url.searchParams.getAll("keywords");
3697
+ const command = url.searchParams.get("command") || undefined;
3698
+ const domain = url.searchParams.get("domain") || undefined;
3699
+ const result = store.queryResonance({ files, keywords, command, domain });
3700
+ return new Response(JSON.stringify(result), { headers });
3701
+ }
3702
+ if (url.pathname === "/query" && req.method === "POST") {
3703
+ store.reloadIfNeeded();
3704
+ const body = await req.json();
3705
+ let files = Array.isArray(body.files) ? body.files : [];
3706
+ if (body.autoGit === true && files.length === 0) {
3707
+ files = await detectModifiedFiles(body.cwd);
3708
+ }
3709
+ const result = store.queryResonance({
3710
+ files,
3711
+ keywords: body.keywords,
3712
+ command: body.command,
3713
+ domain: body.domain,
3714
+ propagateRelations: body.propagateRelations,
3715
+ maxHops: body.maxHops
3716
+ });
3717
+ const format = url.searchParams.get("format") || body.format;
3718
+ if (format === "markdown" || format === "md") {
3719
+ return new Response(formatResonanceForAgent(result, false), {
3720
+ headers: { ...headers, "Content-Type": "text/markdown; charset=utf-8" }
3721
+ });
3722
+ }
3723
+ if (format === "compact") {
3724
+ return new Response(formatResonanceForAgent(result, true), { headers });
3725
+ }
3726
+ return new Response(JSON.stringify(result), { headers });
3727
+ }
3728
+ if (url.pathname === "/assert" && req.method === "POST") {
3729
+ store.reloadIfNeeded();
3730
+ const body = await req.json();
3731
+ store.assertRule(body);
3732
+ return new Response(JSON.stringify({ ok: true, id: body.id }), { headers });
3733
+ }
3734
+ if (url.pathname === "/audit") {
3735
+ store.reloadIfNeeded();
3736
+ const auditResult = store.audit();
3737
+ return new Response(JSON.stringify(auditResult), { headers });
3738
+ }
3739
+ if (url.pathname === "/sync" && req.method === "GET") {
3740
+ const rawDb = store.getRawDatabase();
3741
+ return new Response(JSON.stringify(rawDb), { headers });
3742
+ }
3743
+ if (url.pathname === "/sync" && req.method === "POST") {
3744
+ const body = await req.json();
3745
+ const incomingRules = Array.isArray(body.rules) ? body.rules : [];
3746
+ const incomingRelations = Array.isArray(body.relations) ? body.relations : [];
3747
+ const mergeResult = store.mergeDatabase(incomingRules, incomingRelations);
3748
+ return new Response(JSON.stringify({ ok: true, ...mergeResult }), { headers });
3749
+ }
3750
+ if (url.pathname === "/context" && req.method === "GET") {
3751
+ contextStore.reloadIfNeeded();
3752
+ const id = url.searchParams.get("id") || undefined;
3753
+ const sessionId = url.searchParams.get("sessionId") || undefined;
3754
+ const listMode = url.searchParams.get("list") === "true";
3755
+ if (listMode) {
3756
+ const list = contextStore.listCheckpoints({ sessionId });
3757
+ return new Response(JSON.stringify({ ok: true, checkpoints: list }), { headers });
3758
+ }
3759
+ const found = contextStore.getCheckpoint(id || "latest", sessionId);
3760
+ return new Response(JSON.stringify({ ok: Boolean(found), checkpoint: found }), { headers });
3761
+ }
3762
+ if (url.pathname === "/context" && req.method === "POST") {
3763
+ const body = await req.json();
3764
+ const saved = await contextStore.saveCheckpoint(body);
3765
+ return new Response(JSON.stringify({ ok: true, checkpoint: saved }), { headers });
3766
+ }
3767
+ if (url.pathname === "/timeline" && req.method === "GET") {
3768
+ const timeSeries = new CausalTimeSeriesEngine(contextStore, store);
3769
+ const limit = parseInt(url.searchParams.get("limit") || "20", 10);
3770
+ const tl = timeSeries.generateTimeline(limit);
3771
+ return new Response(JSON.stringify({ ok: true, timeline: tl }), { headers });
3772
+ }
3773
+ if (url.pathname === "/graveyard") {
3774
+ const graveyard = new CausalGraveyardStore(store);
3775
+ if (req.method === "GET") {
3776
+ const list = graveyard.getAllTraps();
3777
+ return new Response(JSON.stringify({ ok: true, traps: list }), { headers });
3778
+ }
3779
+ if (req.method === "POST") {
3780
+ const body = await req.json();
3781
+ if (body.action === "scan") {
3782
+ const scanRes = graveyard.scanTraps({
3783
+ files: body.files,
3784
+ keywords: body.keywords
3785
+ });
3786
+ return new Response(JSON.stringify({ ok: true, scan: scanRes }), { headers });
3787
+ }
3788
+ const trap = graveyard.assertTrap(body);
3789
+ return new Response(JSON.stringify({ ok: true, trap }), { headers });
3790
+ }
3791
+ }
3792
+ if (url.pathname === "/branch") {
3793
+ const branchStore = new CausalBranchStore(contextStore);
3794
+ if (req.method === "GET") {
3795
+ return new Response(JSON.stringify({
3796
+ ok: true,
3797
+ current: branchStore.getCurrentBranch(),
3798
+ branches: branchStore.listBranches()
3799
+ }), { headers });
3800
+ }
3801
+ if (req.method === "POST") {
3802
+ const body = await req.json();
3803
+ if (body.action === "create") {
3804
+ const b = await branchStore.createBranch(body.name, {
3805
+ description: body.fromBranch
3806
+ });
3807
+ return new Response(JSON.stringify({ ok: true, branch: b }), { headers });
3808
+ }
3809
+ if (body.action === "switch") {
3810
+ const b = branchStore.switchBranch(body.name);
3811
+ return new Response(JSON.stringify({ ok: true, branch: b }), { headers });
3812
+ }
3813
+ if (body.action === "merge") {
3814
+ const res = await branchStore.mergeBranch(body.name, body.targetBranch);
3815
+ return new Response(JSON.stringify(res), { headers });
3816
+ }
3817
+ if (body.action === "abandon") {
3818
+ const res = branchStore.abandonBranch(body.name, body.failureReason, body.safeAlternative);
3819
+ return new Response(JSON.stringify(res), { headers });
3820
+ }
3821
+ }
3822
+ }
3823
+ if (url.pathname === "/predict" && req.method === "POST") {
3824
+ const predictor = new CausalPredictorEngine(store, contextStore);
3825
+ const body = await req.json();
3826
+ const files = Array.isArray(body.files) ? body.files : [];
3827
+ const maxHops = typeof body.maxHops === "number" ? body.maxHops : 3;
3828
+ const report = predictor.analyzeBlastRadius(files, maxHops);
3829
+ return new Response(JSON.stringify({ ok: true, report }), { headers });
3830
+ }
3831
+ if (url.pathname === "/synthesize" && req.method === "POST") {
3832
+ const synthesizer = new CausalSynthesizerEngine(contextStore, store);
3833
+ const body = await req.json();
3834
+ const cwd = typeof body.cwd === "string" ? body.cwd : process.cwd();
3835
+ if (body.autoCheckpoint === false) {
3836
+ const summary = await synthesizer.inspectGitChanges(cwd);
3837
+ const intent = synthesizer.synthesizeIntent(summary, body.title);
3838
+ return new Response(JSON.stringify({ ok: true, summary, intent }), { headers });
3839
+ }
3840
+ const cp = await synthesizer.autoCheckpoint(cwd, { title: body.title });
3841
+ return new Response(JSON.stringify({ ok: true, checkpoint: cp }), { headers });
3842
+ }
3843
+ if (url.pathname === "/lens" && req.method === "POST") {
3844
+ const lens = new CausalObsidianLensEngine(store, contextStore);
3845
+ const body = await req.json();
3846
+ const vaultPath = typeof body.vaultPath === "string" ? body.vaultPath : undefined;
3847
+ if (body.action === "export") {
3848
+ const res = await lens.exportToVault(vaultPath);
3849
+ return new Response(JSON.stringify(res), { headers });
3850
+ }
3851
+ if (body.action === "import") {
3852
+ const res = await lens.importFromVault(vaultPath);
3853
+ return new Response(JSON.stringify(res), { headers });
3854
+ }
3855
+ const syncRes = await lens.syncWithVault(vaultPath);
3856
+ return new Response(JSON.stringify(syncRes), { headers });
3857
+ }
3858
+ if (url.pathname === "/mcp" && req.method === "POST") {
3859
+ const rpc = await req.json();
3860
+ const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath, options.contextDbPath);
3861
+ if (!responseObj) {
3862
+ return new Response(null, { status: 204, headers });
3863
+ }
3864
+ return new Response(JSON.stringify(responseObj), { headers });
3865
+ }
3866
+ return new Response(JSON.stringify({ error: "Not Found" }), { status: 404, headers });
3867
+ } catch (err) {
3868
+ return new Response(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }), { status: 500, headers });
3869
+ }
3870
+ }
3871
+ });
3872
+ return server;
3873
+ }
3874
+ async function startCausalMcpStdioServer(dbPath, remoteUrl) {
3875
+ const store = new CausalMemoryStore(dbPath);
3876
+ const targetRemote = remoteUrl || CAUSAL_MEMORY_CONFIG.REMOTE_URL;
3877
+ const isRemoteMode = Boolean(targetRemote && targetRemote.trim().length > 0);
3878
+ const activeDaemonUrl = isRemoteMode ? targetRemote.endsWith("/mcp") ? targetRemote : `${targetRemote.replace(/\/+$/, "")}/mcp` : `http://${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST}:${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT}/mcp`;
3879
+ const timeoutMs = isRemoteMode ? CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS : 150;
3880
+ const rl = createInterface({
3881
+ input: process.stdin,
3882
+ terminal: false
3883
+ });
3884
+ for await (const line of rl) {
3885
+ const trimmed = line.trim();
3886
+ if (!trimmed)
3887
+ continue;
3888
+ try {
3889
+ const rpc = JSON.parse(trimmed);
3890
+ let responseObj = null;
3891
+ if (rpc.method === "tools/call") {
3892
+ try {
3893
+ const daemonRes = await fetch(activeDaemonUrl, {
3894
+ method: "POST",
3895
+ headers: { "Content-Type": "application/json" },
3896
+ body: trimmed,
3897
+ signal: AbortSignal.timeout(timeoutMs)
3898
+ });
3899
+ if (daemonRes.ok) {
3900
+ responseObj = await daemonRes.json();
3901
+ }
3902
+ } catch {}
3903
+ }
3904
+ if (!responseObj) {
3905
+ responseObj = await handleMcpJsonRpcRequest(rpc, store, dbPath);
3906
+ }
3907
+ if (responseObj) {
3908
+ process.stdout.write(JSON.stringify(responseObj) + `
3909
+ `);
3910
+ }
3911
+ } catch (error) {
3912
+ const errResp = {
3913
+ jsonrpc: "2.0",
3914
+ id: null,
3915
+ error: {
3916
+ code: -32700,
3917
+ message: `JSON Parse error: ${error instanceof Error ? error.message : String(error)}`
3918
+ }
3919
+ };
3920
+ process.stdout.write(JSON.stringify(errResp) + `
3921
+ `);
3922
+ }
3923
+ }
3924
+ }
3925
+ // memory/causal-migrator.ts
3926
+ import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
3927
+ import { dirname as dirname5 } from "path";
3928
+ function migrateLegacyMemoryGraph(sourceBackupPath = CAUSAL_MEMORY_CONFIG.LEGACY_BACKUP_PATH, targetGlobalPath = CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH, targetLocalPath = CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH) {
3929
+ if (!existsSync7(sourceBackupPath)) {
3930
+ throw new Error(`\uAD6C\uD615 \uBA54\uBAA8\uB9AC \uBC31\uC5C5 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${sourceBackupPath}`);
3931
+ }
3932
+ const raw = readFileSync6(sourceBackupPath, "utf-8");
3933
+ const legacy = JSON.parse(raw);
3934
+ const entities = legacy.entities || [];
3935
+ const legacyRelations = legacy.relations || [];
3936
+ const today = new Date().toISOString().split("T")[0];
3937
+ const seenIds = new Map;
3938
+ const rules = [];
3939
+ for (const ent of entities) {
3940
+ const inferred = CausalMemoryStore.inferRuleFromEntity(ent, today);
3941
+ const baseId = inferred.id;
3942
+ const count = seenIds.get(baseId) || 0;
3943
+ seenIds.set(baseId, count + 1);
3944
+ const uniqueId = count === 0 ? baseId : `${baseId}-${count + 1}`;
3945
+ rules.push({
3946
+ ...inferred,
3947
+ id: uniqueId
3948
+ });
3949
+ }
3950
+ const hasVesperCore = rules.some((r) => r.id === "rule-project_vesper_dsp-vesper-dsp-master-invariants");
3951
+ if (!hasVesperCore) {
3952
+ rules.push({
3953
+ id: "rule-project_vesper_dsp-vesper-dsp-master-invariants",
3954
+ domain: "Project_Vesper_DSP",
3955
+ title: "Vesper_DSP_Master_Invariants",
3956
+ entityType: "Architecture_Invariant",
3957
+ triggers: {
3958
+ files: ["D:/Vesper", "crates/vesper-", "vesper-apo", "dsp-settings.json"],
3959
+ keywords: ["vesper", "dsp", "mmcss", "virtuallock", "\uC624\uB514\uC624", "\uC0AC\uC6B4\uB4DC", "\uB04A\uAE40", "apo"],
3960
+ commands: ["check", "build", "package", "sign"]
3961
+ },
3962
+ invariants: [
3963
+ "\uC624\uB514\uC624 \uBC84\uD37C\uB294 \uBC18\uB4DC\uC2DC Win32 VirtualLock\uC73C\uB85C \uBB3C\uB9AC RAM\uC5D0 \uACE0\uC815\uD558\uC5EC \uB514\uC2A4\uD06C \uC2A4\uC651 \uC544\uC6C3(\uD558\uB4DC \uD398\uC774\uC9C0 \uD3F4\uD2B8)\uC744 \uC6D0\uCC9C \uCC28\uB2E8\uD560 \uAC83",
3964
+ "\uC624\uB514\uC624 \uCC98\uB9AC \uC2A4\uB808\uB4DC\uB294 Windows MMCSS(Pro Audio) \uC2E4\uC2DC\uAC04 \uC2A4\uCF00\uC904\uB7EC\uC5D0 \uD544\uC218 \uB4F1\uB85D\uD560 \uAC83",
3965
+ "\uC124\uC815 \uC800\uC7A5\uC740 \uBE0C\uB77C\uC6B0\uC800 localStorage \uB2E8\uB3C5 \uC758\uC874\uC744 \uAE08\uC9C0\uD558\uACE0 Crash-Safe \uC6D0\uC790\uC801(Atomic) \uD30C\uC77C \uC800\uC7A5(dsp-settings.json)\uC744 \uC0AC\uC6A9\uD560 \uAC83",
3966
+ "Windows \uC167\uB2E4\uC6B4 \uC2DC WM_QUERYENDSESSION / SM_SHUTTINGDOWN \uAC10\uC9C0 \uD6C4 \uC815\uC0C1 \uC885\uB8CC\uB97C \uBCF4\uC7A5\uD560 \uAC83"
3967
+ ],
3968
+ actions: [
3969
+ "1. Cargo Workspace 5\uB300 \uB9C8\uC774\uD06C\uB85C \uD06C\uB808\uC774\uD2B8(vesper-config, vesper-ipc, vesper-audio-core, vesper-system, vesper-tauri) \uACBD\uACC4 \uC900\uC218",
3970
+ "2. \uC218\uC815\uB41C \uD06C\uB808\uC774\uD2B8 \uD2B8\uB799\uB9CC \uC778\uACFC\uC728 \uC99D\uBD84 \uBE4C\uB4DC \uC218\uD589"
3971
+ ],
3972
+ context: [
3973
+ "\uC2E4\uC99D \uD0C0\uAE43 \uACBD\uB85C: D:\\Vesper\\dsp-boot-fix\\dsp",
3974
+ "APO DLL \uBC0F Taskbar \uB124\uC774\uD2F0\uBE0C \uBC14\uC774\uB108\uB9AC \uC544\uD0A4\uD14D\uCC98 \uBB34\uACB0\uC131(x64 PE) \uAC80\uC99D \uD544\uC218"
3975
+ ],
3976
+ lastVerified: today
3977
+ });
3978
+ }
3979
+ const hasSynapseCore = rules.some((r) => r.id === "rule-project_synapse_kit-synapse-kit-core-invariants");
3980
+ if (!hasSynapseCore) {
3981
+ rules.push({
3982
+ id: "rule-project_synapse_kit-synapse-kit-core-invariants",
3983
+ domain: "Project_Synapse_Kit",
3984
+ title: "Synapse_Kit_Core_Invariants",
3985
+ entityType: "Framework_Invariant",
3986
+ triggers: {
3987
+ files: ["D:/synapse", "D:/synapse-site", "synapse.config.ts"],
3988
+ keywords: ["synapse", "synapse-kit", "\uC2DC\uB0C5\uC2A4", "\uC778\uACFC\uC728", "causal", "\uCEF4\uD30C\uC77C\uB7EC", "\uCCB4\uCEE4"],
3989
+ commands: ["check", "build", "package", "sign", "init", "memory"]
3990
+ },
3991
+ invariants: [
3992
+ "Git \uC0C1\uD0DC \uC2A4\uCE94 \uC2DC \uBC18\uB4DC\uC2DC git status --porcelain -uall \uD50C\uB798\uADF8\uB97C \uC0AC\uC6A9\uD558\uC5EC \uBBF8\uCD94\uC801 \uD558\uC704 \uB514\uB809\uD130\uB9AC \uD30C\uC77C \uB204\uB77D\uC744 \uBC29\uC9C0\uD560 \uAC83",
3993
+ "AI \uC5D0\uC774\uC804\uD2B8 \uC5F0\uB3D9 \uC2DC --compact \uBAA8\uB4DC\uB97C \uD1B5\uD574 \uC131\uACF5 \uC2DC 11\uD1A0\uD070 \uB2E8\uC77C \uB77C\uC778 JSON\uC744 \uBC18\uD658\uD558\uC5EC \uD1A0\uD070\uC744 99.8% \uC808\uAC10\uD560 \uAC83",
3994
+ "\uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800 \uBC0F \uB7F0\uD0C0\uC784\uC740 npm/npx\uB97C \uC808\uB300 \uC0AC\uC6A9\uD558\uC9C0 \uC54A\uACE0 \uBC18\uB4DC\uC2DC bun\uC744 \uC0AC\uC6A9\uD560 \uAC83"
3995
+ ],
3996
+ actions: [
3997
+ "1. \uCF54\uB4DC \uBCC0\uACBD \uD6C4 bun D:/synapse/dist/cli.js check --compact\uB85C \uBB34\uACB0\uC131 \uAC80\uC99D",
3998
+ "2. \uBA54\uBAA8\uB9AC \uC870\uD68C \uC2DC synapse memory query\uB85C \uACF5\uBA85 \uADDC\uCE59\uB9CC \uCD5C\uC18C \uD1A0\uD070 \uC8FC\uC785"
3999
+ ],
4000
+ context: [
4001
+ "\uC5D4\uC9C4 \uCF54\uC5B4 \uACBD\uB85C: D:\\synapse",
4002
+ "\uACF5\uC2DD \uBB38\uC11C \uBC0F \uC704\uD0A4 \uACBD\uB85C: D:\\synapse-site (KO/EN \uB2E4\uAD6D\uC5B4 \uB85C\uCF00\uC77C \uC790\uB3D9 \uBD84\uAE30)"
4003
+ ],
4004
+ lastVerified: today
4005
+ });
4006
+ }
4007
+ const relations = [...legacyRelations];
4008
+ const coreRelations = [
4009
+ {
4010
+ from: "POE2_AutoFilter_Project",
4011
+ to: "POE2_AutoFilter_ReleaseProcess",
4012
+ relationType: "governed_by_workflow"
4013
+ },
4014
+ {
4015
+ from: "POE2_AutoFilter_ReleaseProcess",
4016
+ to: "POE2_AutoFilter_Updater",
4017
+ relationType: "triggers_update_of"
4018
+ },
4019
+ {
4020
+ from: "POE2_AutoFilter_ReleaseProcess",
4021
+ to: "POE2_AutoFilter_KnownBugs",
4022
+ relationType: "verifies_and_notifies"
4023
+ },
4024
+ {
4025
+ from: "Synapse_Kit_Core_Invariants",
4026
+ to: "Vesper_DSP_Master_Invariants",
4027
+ relationType: "verifies_and_builds"
4028
+ }
4029
+ ];
4030
+ for (const cr of coreRelations) {
4031
+ const exists = relations.some((r) => r.from === cr.from && r.to === cr.to && r.relationType === cr.relationType);
4032
+ if (!exists) {
4033
+ relations.push(cr);
4034
+ }
4035
+ }
4036
+ const db = {
4037
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
4038
+ updatedAt: new Date().toISOString(),
4039
+ totalRules: rules.length,
4040
+ totalRelations: relations.length,
4041
+ rules,
4042
+ relations
4043
+ };
4044
+ const serialized = JSON.stringify(db, null, 2);
4045
+ for (const outPath of [targetGlobalPath, targetLocalPath]) {
4046
+ const dir = dirname5(outPath);
4047
+ if (!existsSync7(dir)) {
4048
+ mkdirSync6(dir, { recursive: true });
4049
+ }
4050
+ writeFileSync6(outPath, serialized, "utf-8");
4051
+ }
4052
+ const alwaysActiveCount = rules.filter((r) => r.triggers.alwaysActive).length;
4053
+ const domains = Array.from(new Set(rules.map((r) => r.domain)));
4054
+ return {
4055
+ ok: true,
4056
+ totalRules: rules.length,
4057
+ totalRelations: relations.length,
4058
+ alwaysActiveCount,
4059
+ domains
4060
+ };
4061
+ }
4062
+ // memory/causal-benchmark.ts
4063
+ var RESET = "\x1B[0m";
4064
+ var BOLD = "\x1B[1m";
4065
+ var CYAN = "\x1B[36m";
4066
+ var GREEN = "\x1B[32m";
4067
+ var YELLOW = "\x1B[33m";
4068
+ var RED = "\x1B[31m";
4069
+ var GRAY = "\x1B[90m";
4070
+ function calcPercentile(sorted, p) {
4071
+ if (sorted.length === 0)
4072
+ return 0;
4073
+ const idx = Math.min(sorted.length - 1, Math.floor(p / 100 * sorted.length));
4074
+ return Math.round(sorted[idx] * 1000) / 1000;
4075
+ }
4076
+ function generateSyntheticDatabase(ruleCount, relationCount) {
4077
+ const domains = [
4078
+ "Project_POE2_AutoFilter",
4079
+ "Project_Vesper_DSP",
4080
+ "Project_Synapse_Kit",
4081
+ "BioPhys_Research_And_Architecture",
4082
+ "Hardware_And_PCB",
4083
+ "Knowledge_Base"
4084
+ ];
4085
+ const rules = [
4086
+ {
4087
+ id: "rule-always-1",
4088
+ domain: "User_Preferences_And_Rules",
4089
+ title: "Global_Preference_1",
4090
+ triggers: { alwaysActive: true },
4091
+ invariants: ["\uD56D\uC0C1 \uD55C\uAD6D\uC5B4 \uC6B0\uC120 \uCD9C\uB825", "bun \uD544\uC218 \uC0AC\uC6A9"],
4092
+ actions: [],
4093
+ context: []
4094
+ },
4095
+ {
4096
+ id: "rule-always-2",
4097
+ domain: "User_Preferences_And_Rules",
4098
+ title: "Global_Preference_2",
4099
+ triggers: { alwaysActive: true },
4100
+ invariants: ["console.log \uB514\uBC84\uADF8 \uCF54\uB4DC \uAE08\uC9C0", "\uD558\uB4DC\uCF54\uB529 \uAE08\uC9C0"],
4101
+ actions: [],
4102
+ context: []
4103
+ },
4104
+ {
4105
+ id: "rule-always-3",
4106
+ domain: "User_Preferences_And_Rules",
4107
+ title: "Global_Preference_3",
4108
+ triggers: { alwaysActive: true },
4109
+ invariants: ["100% \uC21C\uC218 Rust \uBC31\uC5D4\uB4DC \uC6D0\uCE59"],
4110
+ actions: [],
4111
+ context: []
4112
+ },
4113
+ {
4114
+ id: "rule-always-4",
4115
+ domain: "User_Preferences_And_Rules",
4116
+ title: "Global_Preference_4",
4117
+ triggers: { alwaysActive: true },
4118
+ invariants: ["\uAC80\uC99D \uCD5C\uC6B0\uC120 \uC6D0\uCE59 \uC900\uC218"],
4119
+ actions: [],
4120
+ context: []
4121
+ }
4122
+ ];
4123
+ for (let i = 4;i < ruleCount; i++) {
4124
+ const dom = domains[i % domains.length];
4125
+ const moduleIdx = i % 500;
4126
+ rules.push({
4127
+ id: `rule-synth-${i}`,
4128
+ domain: dom,
4129
+ title: `Synth_Node_${i}`,
4130
+ triggers: {
4131
+ files: [`packages/mod_${moduleIdx}/src/file_${i}.rs`],
4132
+ keywords: [`kw_${i}`, `topic_${i % 250}`],
4133
+ commands: i % 20 === 0 ? ["build"] : i % 20 === 1 ? ["release"] : ["check"]
4134
+ },
4135
+ invariants: [`\uBD88\uBCC0 \uACC4\uC57D #${i}: \uBA54\uBAA8\uB9AC \uC548\uC804\uC131 \uBC0F \uD0C0\uC785 \uACBD\uACC4 \uBCF4\uC7A5`],
4136
+ actions: [`1. \uBAA8\uB4C8 ${moduleIdx} \uC815\uC801 \uAC80\uC99D \uC218\uD589`],
4137
+ context: [`\uD569\uC131 \uBCA4\uCE58\uB9C8\uD06C \uB178\uB4DC \uCEE8\uD14D\uC2A4\uD2B8 \uB370\uC774\uD130 #${i}`],
4138
+ lastVerified: "2026-11-01"
4139
+ });
4140
+ }
4141
+ const relations = [];
4142
+ relations.push({ from: "Synth_Node_10", to: "Synth_Node_11", relationType: "triggers_hop_1" }, { from: "Synth_Node_11", to: "Synth_Node_12", relationType: "triggers_hop_2" }, { from: "Synth_Node_12", to: "Synth_Node_13", relationType: "triggers_hop_3" }, { from: "Synth_Node_13", to: "Synth_Node_14", relationType: "triggers_hop_4" }, { from: "Synth_Node_14", to: "Synth_Node_15", relationType: "triggers_hop_5" }, { from: "Synth_Node_15", to: "Synth_Node_10", relationType: "circular_feedback_edge" });
4143
+ for (let j = relations.length;j < relationCount; j++) {
4144
+ const fromIdx = 4 + j % (ruleCount - 10);
4145
+ const toIdx = fromIdx + 1 + j % 5;
4146
+ relations.push({
4147
+ from: `Synth_Node_${fromIdx}`,
4148
+ to: `Synth_Node_${toIdx}`,
4149
+ relationType: "causal_impact_on"
4150
+ });
4151
+ }
4152
+ return {
4153
+ version: "2.1.0",
4154
+ updatedAt: new Date().toISOString(),
4155
+ totalRules: rules.length,
4156
+ totalRelations: relations.length,
4157
+ rules,
4158
+ relations
4159
+ };
4160
+ }
4161
+ async function executeOverloadBenchmark(customConfig, silent = false) {
4162
+ const config = {
4163
+ ruleScale: customConfig?.ruleScale ?? 1e4,
4164
+ relationScale: customConfig?.relationScale ?? 20000,
4165
+ concurrency: customConfig?.concurrency ?? 50,
4166
+ totalRequests: customConfig?.totalRequests ?? 5000,
4167
+ fileInputScale: customConfig?.fileInputScale ?? 1e4,
4168
+ crudIterations: customConfig?.crudIterations ?? 5000
4169
+ };
4170
+ const log = (msg) => {
4171
+ if (!silent)
4172
+ process.stdout.write(msg);
4173
+ };
4174
+ const benchStart = performance.now();
4175
+ const scenarios = [];
4176
+ const isExtreme = config.ruleScale >= 1e5;
4177
+ const modeTitle = isExtreme ? "\uC635\uC158 2 \uADF9\uD55C \uD55C\uACC4 \uB3CC\uD30C \uC2A4\uD2B8\uB808\uC2A4 \uBCA4\uCE58\uB9C8\uD06C (Stress Max)" : "\uC635\uC158 1 \uD45C\uC900 \uACFC\uBD80\uD558 \uC2A4\uD2B8\uB808\uC2A4 \uBCA4\uCE58\uB9C8\uD06C";
4178
+ const sloP50Ms = isExtreme ? 2 : 0.8;
4179
+ const sloP99Ms = isExtreme ? 5 : 1.5;
4180
+ const sloRps = isExtreme ? 3000 : 5000;
4181
+ const sloHopMs = isExtreme ? 100 : 30;
4182
+ const sloHeapDeltaMB = isExtreme ? 8 : 3;
4183
+ log(`
4184
+ ${BOLD}${CYAN}\uD83D\uDD25 [Causal Memory 2.1] ${modeTitle} \uAC00\uB3D9${RESET}
4185
+ `);
4186
+ log(`${GRAY}\u2022 \uC2A4\uD399: \uADDC\uCE59 ${config.ruleScale.toLocaleString()}\uAC1C | \uAC04\uC120 ${config.relationScale.toLocaleString()}\uAC1C | \uB3D9\uC2DC\uC131 ${config.concurrency} \uC6CC\uCEE4 | \uC694\uCCAD ${config.totalRequests.toLocaleString()}\uD68C | \uBCC0\uACBD\uD30C\uC77C ${config.fileInputScale.toLocaleString()}\uAC1C${RESET}
4187
+ `);
4188
+ log(`${GRAY}${"\u2501".repeat(84)}${RESET}
4189
+
4190
+ `);
4191
+ log(`${BOLD}${YELLOW}\uD83D\uDCCC [\uC2DC\uB098\uB9AC\uC624 1] ${config.ruleScale.toLocaleString()}\uAC1C \uADDC\uCE59 & ${config.relationScale.toLocaleString()}\uAC1C \uAC04\uC120 \uC801\uC7AC \uBC0F O(1) \uC5ED\uC778\uB371\uC2A4 \uACF5\uBA85 \uD14C\uC2A4\uD2B8${RESET}
4192
+ `);
4193
+ const genStart = performance.now();
4194
+ const synthDb = generateSyntheticDatabase(config.ruleScale, config.relationScale);
4195
+ const genDurationMs = Math.round((performance.now() - genStart) * 100) / 100;
4196
+ const indexStart = performance.now();
4197
+ const scaleStore = new CausalMemoryStore(undefined, synthDb);
4198
+ const indexBuildMs = Math.round((performance.now() - indexStart) * 100) / 100;
4199
+ const queryLatencies = [];
4200
+ let lastResonance = scaleStore.queryResonance({ keywords: ["kw_10"] });
4201
+ for (let i = 0;i < 1000; i++) {
4202
+ const targetKw = `kw_${10 + i % 5000}`;
4203
+ const qStart = performance.now();
4204
+ lastResonance = scaleStore.queryResonance({ keywords: [targetKw] });
4205
+ queryLatencies.push(performance.now() - qStart);
4206
+ }
4207
+ queryLatencies.sort((a, b) => a - b);
4208
+ const s1P50 = calcPercentile(queryLatencies, 50);
4209
+ const s1P95 = calcPercentile(queryLatencies, 95);
4210
+ const s1P99 = calcPercentile(queryLatencies, 99);
4211
+ const s1Passed = s1P50 <= sloP50Ms && s1P99 <= sloP99Ms && parseFloat(lastResonance.tokenDietSavingsRatio) >= 99;
4212
+ scenarios.push({
4213
+ name: "\uC2DC\uB098\uB9AC\uC624 1: \uB300\uADDC\uBAA8 \uBCFC\uB968 \uC801\uC7AC \uBC0F O(1) \uACF5\uBA85 \uCFFC\uB9AC",
4214
+ passed: s1Passed,
4215
+ details: {
4216
+ totalRules: config.ruleScale,
4217
+ totalRelations: config.relationScale,
4218
+ syntheticGenMs: genDurationMs,
4219
+ indexBuildMs,
4220
+ p50Ms: s1P50,
4221
+ p95Ms: s1P95,
4222
+ p99Ms: s1P99,
4223
+ tokenSavings: lastResonance.tokenDietSavingsRatio
4224
+ }
4225
+ });
4226
+ log(` \u2022 \uC5ED\uC778\uB371\uC2A4 \uAD6C\uCD95 \uC2DC\uAC04 : ${BOLD}${GREEN}${indexBuildMs}ms${RESET} (${config.ruleScale.toLocaleString()} Rules + ${config.relationScale.toLocaleString()} Relations)
4227
+ `);
4228
+ log(` \u2022 \uACF5\uBA85 \uCFFC\uB9AC \uB808\uC774\uD134\uC2DC : ${BOLD}p50=${s1P50}ms | p95=${s1P95}ms | p99=${s1P99}ms${RESET} (\uBAA9\uD45C: p50\u2264${sloP50Ms}ms, p99\u2264${sloP99Ms}ms)
4229
+ `);
4230
+ log(` \u2022 \uD1A0\uD070 \uC808\uAC10\uB960 : ${BOLD}${GREEN}${lastResonance.tokenDietSavingsRatio}${RESET} (${lastResonance.matchedCount}/${config.ruleScale}\uAC1C\uB9CC \uC815\uBC00 \uACF5\uBA85)
4231
+ `);
4232
+ log(` \u2022 \uD310\uC815 : ${s1Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
4233
+
4234
+ `);
4235
+ log(`${BOLD}${YELLOW}\uD83D\uDCCC [\uC2DC\uB098\uB9AC\uC624 2] \uB3D9\uC2DC ${config.concurrency} \uD074\uB77C\uC774\uC5B8\uD2B8 \xD7 ${config.totalRequests.toLocaleString()}\uD68C HTTP/MCP \uB370\uBAAC \uD3ED\uACA9 \uD14C\uC2A4\uD2B8${RESET}
4236
+ `);
4237
+ const benchPort = 4288;
4238
+ const server = startCausalServer({ port: benchPort });
4239
+ let successCount = 0;
4240
+ let errorCount = 0;
4241
+ const httpLatencies = [];
4242
+ const requestsPerWorker = Math.floor(config.totalRequests / config.concurrency);
4243
+ for (let w = 0;w < 10; w++) {
4244
+ await fetch(`http://127.0.0.1:${benchPort}/query`, {
4245
+ method: "POST",
4246
+ headers: { "Content-Type": "application/json" },
4247
+ body: JSON.stringify({ keywords: ["poe2"], format: "compact" })
4248
+ });
4249
+ }
4250
+ const concStart = performance.now();
4251
+ try {
4252
+ const workerPromises = Array.from({ length: config.concurrency }, async (_, workerId) => {
4253
+ const sampleQueries = [
4254
+ { keywords: ["poe2", "updater"], format: "compact" },
4255
+ { files: ["crates/vesper-audio-core/src/lib.rs"], format: "compact" },
4256
+ { keywords: ["synapse", "causal"], format: "compact" },
4257
+ { keywords: ["pcb", "kicad"], format: "compact" }
4258
+ ];
4259
+ for (let r = 0;r < requestsPerWorker; r++) {
4260
+ const qPayload = sampleQueries[(workerId + r) % sampleQueries.length];
4261
+ const reqT0 = performance.now();
4262
+ try {
4263
+ const resp = await fetch(`http://127.0.0.1:${benchPort}/query`, {
4264
+ method: "POST",
4265
+ headers: { "Content-Type": "application/json" },
4266
+ body: JSON.stringify(qPayload)
4267
+ });
4268
+ if (resp.ok) {
4269
+ await resp.text();
4270
+ successCount++;
4271
+ } else {
4272
+ errorCount++;
4273
+ }
4274
+ } catch {
4275
+ errorCount++;
4276
+ }
4277
+ httpLatencies.push(performance.now() - reqT0);
4278
+ }
4279
+ });
4280
+ await Promise.all(workerPromises);
4281
+ } finally {
4282
+ server.stop();
4283
+ }
4284
+ const concTotalSec = (performance.now() - concStart) / 1000;
4285
+ const rps = Math.round(successCount / concTotalSec);
4286
+ httpLatencies.sort((a, b) => a - b);
4287
+ const s2P50 = calcPercentile(httpLatencies, 50);
4288
+ const s2P95 = calcPercentile(httpLatencies, 95);
4289
+ const s2P99 = calcPercentile(httpLatencies, 99);
4290
+ const s2Passed = errorCount === 0 && successCount === config.totalRequests && rps >= sloRps;
4291
+ scenarios.push({
4292
+ name: "\uC2DC\uB098\uB9AC\uC624 2: \uACE0\uB3D9\uC2DC\uC131 \uB370\uBAAC \uCC98\uB9AC\uB7C9",
4293
+ passed: s2Passed,
4294
+ details: {
4295
+ concurrency: config.concurrency,
4296
+ totalRequests: config.totalRequests,
4297
+ successCount,
4298
+ errorCount,
4299
+ durationSec: Math.round(concTotalSec * 100) / 100,
4300
+ rps,
4301
+ httpP50Ms: s2P50,
4302
+ httpP95Ms: s2P95,
4303
+ httpP99Ms: s2P99
4304
+ }
4305
+ });
4306
+ log(` \u2022 \uCD1D \uCC98\uB9AC \uC694\uCCAD : ${BOLD}${GREEN}${successCount.toLocaleString()} / ${config.totalRequests.toLocaleString()} \uC131\uACF5${RESET} (\uC624\uB958\uC728: ${errorCount === 0 ? "0.00%" : errorCount})
4307
+ `);
4308
+ log(` \u2022 \uCD08\uB2F9 \uCC98\uB9AC\uB7C9 (RPS) : ${BOLD}${CYAN}${rps.toLocaleString()} req/sec${RESET} (\uCD1D \uC18C\uC694: ${concTotalSec.toFixed(2)}s, \uBAA9\uD45C: \u2265${sloRps.toLocaleString()} req/s)
4309
+ `);
4310
+ log(` \u2022 HTTP \uC655\uBCF5 \uB808\uC774\uD134\uC2DC : ${BOLD}p50=${s2P50}ms | p95=${s2P95}ms | p99=${s2P99}ms${RESET}
4311
+ `);
4312
+ log(` \u2022 \uD310\uC815 : ${s2Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
4313
+
4314
+ `);
4315
+ log(`${BOLD}${YELLOW}\uD83D\uDCCC [\uC2DC\uB098\uB9AC\uC624 3] ${config.fileInputScale.toLocaleString()}\uAC1C \uBCC0\uACBD \uD30C\uC77C \uB3D9\uC2DC \uC785\uB825 & 5-Hop \uB2E4\uC911 \uCCB4\uC778/\uC21C\uD658 \uCC38\uC870 \uC2A4\uD2B8\uB808\uC2A4${RESET}
4316
+ `);
4317
+ const massiveFiles = [];
4318
+ for (let f = 0;f < config.fileInputScale; f++) {
4319
+ massiveFiles.push(`services/microservice_${f % 100}/src/handler_${f}.ts`);
4320
+ }
4321
+ massiveFiles[Math.floor(config.fileInputScale / 2)] = "packages/mod_10/src/file_10.rs";
4322
+ const hopStart = performance.now();
4323
+ const multiHopRes = scaleStore.queryResonance({
4324
+ files: massiveFiles,
4325
+ propagateRelations: true,
4326
+ maxHops: 5
4327
+ });
4328
+ const hopDurationMs = Math.round((performance.now() - hopStart) * 100) / 100;
4329
+ const matchedTitles = new Set(multiHopRes.resonanceRules.map((r) => r.title));
4330
+ const all5HopsPropagated = matchedTitles.has("Synth_Node_10") && matchedTitles.has("Synth_Node_11") && matchedTitles.has("Synth_Node_12") && matchedTitles.has("Synth_Node_13") && matchedTitles.has("Synth_Node_14") && matchedTitles.has("Synth_Node_15");
4331
+ const s3Passed = all5HopsPropagated && hopDurationMs <= sloHopMs;
4332
+ scenarios.push({
4333
+ name: "\uC2DC\uB098\uB9AC\uC624 3: \uB300\uADDC\uBAA8 \uD30C\uC77C & 5-Hop \uB2E4\uC911 \uC778\uACFC \uC804\uD30C",
4334
+ passed: s3Passed,
4335
+ details: {
4336
+ inputFilesCount: config.fileInputScale,
4337
+ maxHops: 5,
4338
+ traversalDurationMs: hopDurationMs,
4339
+ matchedRulesCount: multiHopRes.matchedCount,
4340
+ activeRelationsCount: multiHopRes.activeRelations.length,
4341
+ all5HopsResolved: all5HopsPropagated ? "YES (Circular Loop Safe)" : "NO",
4342
+ tokenSavings: multiHopRes.tokenDietSavingsRatio
4343
+ }
4344
+ });
4345
+ log(` \u2022 \uB300\uB7C9 \uD30C\uC77C + 5-Hop \uC18C\uC694 : ${BOLD}${GREEN}${hopDurationMs}ms${RESET} (\uBAA9\uD45C: \u2264${sloHopMs}ms)
4346
+ `);
4347
+ log(` \u2022 5\uB2E8\uACC4 \uC5F0\uC1C4 \uC804\uD30C \uAC80\uC99D : ${BOLD}${all5HopsPropagated ? `${GREEN}Node_10 \u2794 Node_15 (5-Hop) \uBC0F \uC21C\uD658 \uCC38\uC870 \uBC29\uC5B4 100% \uC131\uACF5` : `${RED}\uC2E4\uD328`}${RESET}
4348
+ `);
4349
+ log(` \u2022 \uC815\uBC00 \uACA9\uB9AC \uC808\uAC10\uB960 : ${BOLD}${GREEN}${multiHopRes.tokenDietSavingsRatio}${RESET} (${multiHopRes.matchedCount}/${config.ruleScale}\uAC1C\uB9CC \uD65C\uC131\uD654)
4350
+ `);
4351
+ log(` \u2022 \uD310\uC815 : ${s3Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
4352
+
4353
+ `);
4354
+ log(`${BOLD}${YELLOW}\uD83D\uDCCC [\uC2DC\uB098\uB9AC\uC624 4] ${config.crudIterations.toLocaleString()}\uD68C \uC5F0\uC18D CRUD \uD2B8\uB79C\uC7AD\uC158 & \uD799 \uBA54\uBAA8\uB9AC \uB204\uC218 \uAC10\uC0AC (Zero-Leak Audit)${RESET}
4355
+ `);
4356
+ if (typeof Bun !== "undefined" && typeof Bun.gc === "function") {
4357
+ Bun.gc(true);
4358
+ }
4359
+ const baseMem = process.memoryUsage();
4360
+ const baseHeapMB = baseMem.heapUsed / (1024 * 1024);
4361
+ const crudStore = new CausalMemoryStore(undefined, {
4362
+ version: "2.1.0",
4363
+ updatedAt: new Date().toISOString(),
4364
+ totalRules: 0,
4365
+ totalRelations: 0,
4366
+ rules: [],
4367
+ relations: []
4368
+ });
4369
+ const crudStart = performance.now();
4370
+ for (let c = 0;c < config.crudIterations; c++) {
4371
+ const tempName = `Ephemeral_Entity_${c % 50}`;
4372
+ crudStore.createEntities([
4373
+ {
4374
+ name: tempName,
4375
+ entityType: "StressTest",
4376
+ observations: [`\uC808\uB300 \uADDC\uCE59 #${c}`, `1. \uC2E4\uD589 \uB2E8\uACC4 #${c}`]
4377
+ }
4378
+ ], false);
4379
+ crudStore.addObservations([{ entityName: tempName, contents: [`\uCD94\uAC00 \uAD00\uCC30 #${c}`] }], false);
4380
+ crudStore.queryResonance({ keywords: [tempName] });
4381
+ crudStore.deleteEntities([tempName], false);
4382
+ }
4383
+ const crudDurationMs = Math.round((performance.now() - crudStart) * 100) / 100;
4384
+ if (typeof Bun !== "undefined" && typeof Bun.gc === "function") {
4385
+ Bun.gc(true);
4386
+ }
4387
+ const afterMem = process.memoryUsage();
4388
+ const afterHeapMB = afterMem.heapUsed / (1024 * 1024);
4389
+ const rssMB = Math.round(afterMem.rss / (1024 * 1024) * 100) / 100;
4390
+ const heapDeltaMB = Math.round(Math.max(0, afterHeapMB - baseHeapMB) * 100) / 100;
4391
+ const s4Passed = heapDeltaMB <= sloHeapDeltaMB && crudStore.getAllRules().length === 0;
4392
+ scenarios.push({
4393
+ name: "\uC2DC\uB098\uB9AC\uC624 4: \uC5F0\uC18D CRUD \uB0B4\uAD6C\uB3C4 \uBC0F \uD799 \uB204\uC218 \uAC10\uC0AC",
4394
+ passed: s4Passed,
4395
+ details: {
4396
+ crudCycles: config.crudIterations,
4397
+ totalOperations: config.crudIterations * 4,
4398
+ durationMs: crudDurationMs,
4399
+ baseHeapMB: Math.round(baseHeapMB * 100) / 100,
4400
+ afterHeapMB: Math.round(afterHeapMB * 100) / 100,
4401
+ heapDeltaMB,
4402
+ rssMB,
4403
+ residualRules: crudStore.getAllRules().length
4404
+ }
4405
+ });
4406
+ log(` \u2022 \uCD1D \uC218\uD589 \uC5F0\uC0B0 \uC218 : ${BOLD}${(config.crudIterations * 4).toLocaleString()}\uD68C CRUD \uC5F0\uC0B0${RESET} (\uC18C\uC694: ${crudDurationMs}ms)
4407
+ `);
4408
+ log(` \u2022 \uD799 \uBA54\uBAA8\uB9AC \uBCC0\uD654\uB7C9 : ${BOLD}${GREEN}\u0394 ${heapDeltaMB} MB${RESET} (\uC2DC\uC791: ${baseHeapMB.toFixed(2)}MB \u2794 \uC885\uB8CC: ${afterHeapMB.toFixed(2)}MB, \uBAA9\uD45C: \u2264${sloHeapDeltaMB.toFixed(1)}MB)
4409
+ `);
4410
+ log(` \u2022 \uD504\uB85C\uC138\uC2A4 RSS \uC810\uC720 : ${BOLD}${rssMB} MB${RESET}
4411
+ `);
4412
+ log(` \u2022 \uD310\uC815 : ${s4Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
4413
+
4414
+ `);
4415
+ const totalDurationMs = Math.round((performance.now() - benchStart) * 100) / 100;
4416
+ const allPassed = scenarios.every((s) => s.passed);
4417
+ log(`${GRAY}${"\u2501".repeat(84)}${RESET}
4418
+ `);
4419
+ log(`${BOLD}${allPassed ? GREEN : RED}\uD83C\uDFC6 [\uCD5C\uC885 \uBCA4\uCE58\uB9C8\uD06C \uACB0\uACFC] 4\uB300 \uACFC\uBD80\uD558 \uCC28\uC6D0 \uC804\uC218 ${allPassed ? "ALL PASS (100% \uD569\uACA9)" : "\uC77C\uBD80 \uBBF8\uB2EC"} (\uCD1D \uC18C\uC694 \uC2DC\uAC04: ${(totalDurationMs / 1000).toFixed(2)}s)${RESET}
4420
+
4421
+ `);
4422
+ return {
4423
+ timestamp: new Date().toISOString(),
4424
+ config,
4425
+ allPassed,
4426
+ totalDurationMs,
4427
+ scenarios
4428
+ };
4429
+ }
4430
+ export {
4431
+ CAUSAL_MCP_TOOLS,
4432
+ CAUSAL_MEMORY_CONFIG,
4433
+ CausalBranchStore,
4434
+ CausalGraveyardStore,
4435
+ CausalMemoryStore,
4436
+ CausalObsidianLensEngine,
4437
+ CausalPredictorEngine,
4438
+ CausalSynthesizerEngine,
4439
+ CausalTimeSeriesEngine,
4440
+ ContextCheckpointStore,
4441
+ detectGitHeadInfo,
4442
+ detectModifiedFiles,
4443
+ executeOverloadBenchmark,
4444
+ formatResonanceForAgent,
4445
+ generateSyntheticDatabase,
4446
+ handleMcpJsonRpcRequest,
4447
+ migrateLegacyMemoryGraph,
4448
+ runCausalQuery,
4449
+ startCausalMcpStdioServer,
4450
+ startCausalServer
4451
+ };