synapse-kit-memory 0.0.0-stage → 0.2.2

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.
package/dist/cli.js ADDED
@@ -0,0 +1,2230 @@
1
+ // @bun
2
+ var __esm = (fn, res, err) => () => {
3
+ if (fn)
4
+ try {
5
+ res = fn(fn = 0);
6
+ } catch (e) {
7
+ err = [e];
8
+ }
9
+ if (err)
10
+ throw err[0];
11
+ return res;
12
+ };
13
+
14
+ // causal-config.ts
15
+ import { homedir } from "os";
16
+ import { resolve } from "path";
17
+ var CAUSAL_MEMORY_CONFIG;
18
+ var init_causal_config = __esm(() => {
19
+ CAUSAL_MEMORY_CONFIG = {
20
+ SCHEMA_VERSION: process.env.SYNAPSE_MEMORY_VERSION || "2.1.0",
21
+ GLOBAL_DB_PATH: process.env.SYNAPSE_MEMORY_PATH || resolve(homedir(), ".gemini", "antigravity", "causal_memory.json"),
22
+ LEGACY_BACKUP_PATH: process.env.SYNAPSE_MEMORY_BACKUP_PATH || resolve(homedir(), ".gemini", "antigravity", "memory_backup_20261101.json"),
23
+ LOCAL_DB_PATH: resolve(import.meta.dir, "causal-memory.json"),
24
+ DEFAULT_HOST: process.env.SYNAPSE_MEMORY_HOST || "127.0.0.1",
25
+ DEFAULT_PORT: parseInt(process.env.SYNAPSE_MEMORY_PORT || "4242", 10),
26
+ MCP_PROTOCOL_VERSION: process.env.SYNAPSE_MCP_VERSION || "2024-11-05",
27
+ MCP_SERVER_NAME: "synapse-causal-memory",
28
+ MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10)
29
+ };
30
+ });
31
+
32
+ // causal-store.ts
33
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
34
+ import { dirname } from "path";
35
+
36
+ class CausalMemoryStore {
37
+ dbPath;
38
+ database;
39
+ lastMtimeMs = 0;
40
+ alwaysActiveRules = [];
41
+ domainIndex = new Map;
42
+ commandIndex = new Map;
43
+ exactKeywordIndex = new Map;
44
+ exactFileIndex = new Map;
45
+ prefixFileRules = [];
46
+ compiledSearchRules = [];
47
+ titleToRuleMap = new Map;
48
+ idToRuleMap = new Map;
49
+ relationAdjacency = new Map;
50
+ constructor(customPath, initialDatabase) {
51
+ this.dbPath = customPath || (existsSync(CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH) ? CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH : CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH);
52
+ if (initialDatabase) {
53
+ this.database = initialDatabase;
54
+ this.rebuildIndices();
55
+ } else {
56
+ this.database = this.loadDatabase();
57
+ this.rebuildIndices();
58
+ }
59
+ }
60
+ reloadIfNeeded() {
61
+ try {
62
+ if (!existsSync(this.dbPath))
63
+ return false;
64
+ const stat = statSync(this.dbPath);
65
+ if (stat.mtimeMs > this.lastMtimeMs) {
66
+ this.database = this.loadDatabase();
67
+ this.rebuildIndices();
68
+ return true;
69
+ }
70
+ } catch {}
71
+ return false;
72
+ }
73
+ reload() {
74
+ this.database = this.loadDatabase();
75
+ this.rebuildIndices();
76
+ }
77
+ loadFromMemory(db) {
78
+ this.database = db;
79
+ this.rebuildIndices();
80
+ }
81
+ rebuildIndices() {
82
+ this.alwaysActiveRules = [];
83
+ this.domainIndex.clear();
84
+ this.commandIndex.clear();
85
+ this.exactKeywordIndex.clear();
86
+ this.exactFileIndex.clear();
87
+ this.prefixFileRules = [];
88
+ this.compiledSearchRules = [];
89
+ this.titleToRuleMap.clear();
90
+ this.idToRuleMap.clear();
91
+ this.relationAdjacency.clear();
92
+ const addTokenToKeywordIndex = (token, rule) => {
93
+ if (!token)
94
+ return;
95
+ let list = this.exactKeywordIndex.get(token);
96
+ if (!list) {
97
+ list = [];
98
+ this.exactKeywordIndex.set(token, list);
99
+ }
100
+ if (list[list.length - 1] !== rule) {
101
+ list.push(rule);
102
+ }
103
+ };
104
+ for (const rule of this.database.rules) {
105
+ const domainLower = rule.domain.toLowerCase();
106
+ const titleLower = rule.title.toLowerCase();
107
+ const idLower = rule.id.toLowerCase();
108
+ this.titleToRuleMap.set(titleLower, rule);
109
+ this.idToRuleMap.set(idLower, rule);
110
+ let domList = this.domainIndex.get(domainLower);
111
+ if (!domList) {
112
+ domList = [];
113
+ this.domainIndex.set(domainLower, domList);
114
+ }
115
+ domList.push(rule);
116
+ if (rule.triggers.alwaysActive) {
117
+ this.alwaysActiveRules.push(rule);
118
+ }
119
+ if (rule.triggers.commands) {
120
+ for (const cmd of rule.triggers.commands) {
121
+ const cmdLower = cmd.toLowerCase();
122
+ let cmdList = this.commandIndex.get(cmdLower);
123
+ if (!cmdList) {
124
+ cmdList = [];
125
+ this.commandIndex.set(cmdLower, cmdList);
126
+ }
127
+ cmdList.push(rule);
128
+ }
129
+ }
130
+ addTokenToKeywordIndex(titleLower, rule);
131
+ addTokenToKeywordIndex(domainLower, rule);
132
+ for (const part of titleLower.split(/[\s_-]+/)) {
133
+ addTokenToKeywordIndex(part, rule);
134
+ }
135
+ if (rule.triggers.keywords) {
136
+ for (const kw of rule.triggers.keywords) {
137
+ const kwLower = kw.toLowerCase();
138
+ addTokenToKeywordIndex(kwLower, rule);
139
+ for (const sub of kwLower.split(/[\s_-]+/)) {
140
+ addTokenToKeywordIndex(sub, rule);
141
+ }
142
+ }
143
+ }
144
+ if (rule.triggers.files && rule.triggers.files.length > 0) {
145
+ const prefixPatterns = [];
146
+ for (const pattern of rule.triggers.files) {
147
+ const cleaned = (pattern.includes("\\") ? pattern.replaceAll("\\", "/") : pattern).replace(/^\*\*\/|\*\*$|\*$/g, "").replace(/\/+$/, "").toLowerCase();
148
+ if (!cleaned)
149
+ continue;
150
+ let fList = this.exactFileIndex.get(cleaned);
151
+ if (!fList) {
152
+ fList = [];
153
+ this.exactFileIndex.set(cleaned, fList);
154
+ }
155
+ fList.push(rule);
156
+ if (cleaned.endsWith("-") || cleaned.endsWith("_") || cleaned.includes(":")) {
157
+ prefixPatterns.push(cleaned);
158
+ }
159
+ }
160
+ if (prefixPatterns.length > 0) {
161
+ this.prefixFileRules.push({ rule, domainLower, cleanPatterns: prefixPatterns });
162
+ }
163
+ }
164
+ }
165
+ for (const rel of this.database.relations) {
166
+ const fromLower = rel.from.toLowerCase();
167
+ const toLower = rel.to.toLowerCase();
168
+ let fromAdj = this.relationAdjacency.get(fromLower);
169
+ if (!fromAdj) {
170
+ fromAdj = [];
171
+ this.relationAdjacency.set(fromLower, fromAdj);
172
+ }
173
+ fromAdj.push(rel);
174
+ if (toLower !== fromLower) {
175
+ let toAdj = this.relationAdjacency.get(toLower);
176
+ if (!toAdj) {
177
+ toAdj = [];
178
+ this.relationAdjacency.set(toLower, toAdj);
179
+ }
180
+ toAdj.push(rel);
181
+ }
182
+ }
183
+ }
184
+ loadDatabase() {
185
+ try {
186
+ if (existsSync(this.dbPath)) {
187
+ const stat = statSync(this.dbPath);
188
+ this.lastMtimeMs = stat.mtimeMs;
189
+ const raw = readFileSync(this.dbPath, "utf-8");
190
+ const parsed = JSON.parse(raw);
191
+ const rules = Array.isArray(parsed.rules) ? parsed.rules : [];
192
+ const relations = Array.isArray(parsed.relations) ? parsed.relations : [];
193
+ return {
194
+ version: parsed.version || CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
195
+ updatedAt: parsed.updatedAt || new Date().toISOString(),
196
+ totalRules: rules.length,
197
+ totalRelations: relations.length,
198
+ rules,
199
+ relations
200
+ };
201
+ }
202
+ } catch {}
203
+ return {
204
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
205
+ updatedAt: new Date().toISOString(),
206
+ totalRules: 0,
207
+ totalRelations: 0,
208
+ rules: [],
209
+ relations: []
210
+ };
211
+ }
212
+ saveDatabase() {
213
+ try {
214
+ const dir = dirname(this.dbPath);
215
+ if (!existsSync(dir)) {
216
+ mkdirSync(dir, { recursive: true });
217
+ }
218
+ const serialized = JSON.stringify(this.database, null, 2);
219
+ writeFileSync(this.dbPath, serialized, "utf-8");
220
+ this.lastMtimeMs = statSync(this.dbPath).mtimeMs;
221
+ if (this.dbPath === CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH && existsSync(dirname(CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH))) {
222
+ writeFileSync(CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH, serialized, "utf-8");
223
+ }
224
+ } catch (error) {
225
+ throw new Error(`\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB514\uC2A4\uD06C \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
226
+ }
227
+ }
228
+ getAllRules() {
229
+ return this.database.rules;
230
+ }
231
+ getAllRelations() {
232
+ return this.database.relations;
233
+ }
234
+ getStatus() {
235
+ const domainCounts = {};
236
+ for (const [dom, list] of this.domainIndex.entries()) {
237
+ const originalDomainName = list[0]?.domain || dom;
238
+ domainCounts[originalDomainName] = list.length;
239
+ }
240
+ return {
241
+ version: this.database.version,
242
+ updatedAt: this.database.updatedAt,
243
+ totalRules: this.database.rules.length,
244
+ totalRelations: this.database.relations.length,
245
+ dbPath: this.dbPath,
246
+ domains: Object.keys(domainCounts),
247
+ domainCounts
248
+ };
249
+ }
250
+ getCompiledSearchRules() {
251
+ if (this.compiledSearchRules.length === 0 && this.database.rules.length > 0) {
252
+ for (const rule of this.database.rules) {
253
+ const domainLower = rule.domain.toLowerCase();
254
+ const searchableText = [
255
+ rule.title,
256
+ rule.domain,
257
+ ...rule.triggers.keywords || [],
258
+ ...rule.invariants,
259
+ ...rule.actions,
260
+ ...rule.context
261
+ ].join(" ").toLowerCase();
262
+ this.compiledSearchRules.push({ rule, domainLower, searchableText });
263
+ }
264
+ }
265
+ return this.compiledSearchRules;
266
+ }
267
+ queryResonance(options) {
268
+ const startTime = performance.now();
269
+ const targetDomain = options.domain ? options.domain.toLowerCase() : undefined;
270
+ const queryCommand = options.command ? options.command.toLowerCase() : undefined;
271
+ const propagate = options.propagateRelations ?? true;
272
+ const maxHops = options.maxHops ?? 1;
273
+ const matchedRulesMap = new Map;
274
+ const nonAlwaysMatchedTitles = new Set;
275
+ for (const rule of this.alwaysActiveRules) {
276
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
277
+ continue;
278
+ matchedRulesMap.set(rule.id, rule);
279
+ }
280
+ if (queryCommand) {
281
+ const cmdRules = this.commandIndex.get(queryCommand);
282
+ if (cmdRules) {
283
+ for (const rule of cmdRules) {
284
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
285
+ continue;
286
+ matchedRulesMap.set(rule.id, rule);
287
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
288
+ }
289
+ }
290
+ }
291
+ if (options.files && options.files.length > 0 && this.exactFileIndex.size > 0) {
292
+ const hasPrefixRules = this.prefixFileRules.length > 0;
293
+ const checkedDirs = new Set;
294
+ const registerHits = (hits) => {
295
+ if (!hits)
296
+ return;
297
+ for (let i = 0;i < hits.length; i++) {
298
+ const rule = hits[i];
299
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
300
+ continue;
301
+ matchedRulesMap.set(rule.id, rule);
302
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
303
+ }
304
+ };
305
+ let lastTwoSeg = "";
306
+ for (let i = 0;i < options.files.length; i++) {
307
+ const rawFile = options.files[i];
308
+ const nf = (rawFile.includes("\\") ? rawFile.replaceAll("\\", "/") : rawFile).toLowerCase();
309
+ registerHits(this.exactFileIndex.get(nf));
310
+ const firstSlash = nf.indexOf("/");
311
+ if (firstSlash > 0) {
312
+ const secondSlash = nf.indexOf("/", firstSlash + 1);
313
+ const twoSeg = secondSlash > 0 ? nf.slice(0, secondSlash) : nf.slice(0, firstSlash);
314
+ if (twoSeg !== lastTwoSeg) {
315
+ lastTwoSeg = twoSeg;
316
+ if (!checkedDirs.has(twoSeg)) {
317
+ checkedDirs.add(twoSeg);
318
+ registerHits(this.exactFileIndex.get(twoSeg));
319
+ const oneSeg = nf.slice(0, firstSlash);
320
+ if (oneSeg !== twoSeg && !checkedDirs.has(oneSeg)) {
321
+ checkedDirs.add(oneSeg);
322
+ registerHits(this.exactFileIndex.get(oneSeg));
323
+ }
324
+ }
325
+ }
326
+ const lastSlash = nf.lastIndexOf("/");
327
+ registerHits(this.exactFileIndex.get(nf.slice(lastSlash + 1)));
328
+ } else {
329
+ checkedDirs.add(nf);
330
+ }
331
+ }
332
+ if (hasPrefixRules && checkedDirs.size > 0) {
333
+ const prefixArr = Array.from(checkedDirs);
334
+ for (const entry of this.prefixFileRules) {
335
+ if (targetDomain && entry.domainLower !== targetDomain)
336
+ continue;
337
+ if (matchedRulesMap.has(entry.rule.id))
338
+ continue;
339
+ for (const pattern of entry.cleanPatterns) {
340
+ if (prefixArr.some((p) => p.includes(pattern) || pattern.includes(p))) {
341
+ matchedRulesMap.set(entry.rule.id, entry.rule);
342
+ nonAlwaysMatchedTitles.add(entry.rule.title.toLowerCase());
343
+ break;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
349
+ const queryKeywords = (options.keywords || []).flatMap((k) => k.toLowerCase().split(/\s+/)).filter((k) => k.length > 0);
350
+ if (queryKeywords.length > 0) {
351
+ const unindexedKeywords = [];
352
+ for (const qk of queryKeywords) {
353
+ const exactHits = this.exactKeywordIndex.get(qk);
354
+ if (exactHits && exactHits.length > 0) {
355
+ for (const rule of exactHits) {
356
+ if (targetDomain && rule.domain.toLowerCase() !== targetDomain)
357
+ continue;
358
+ matchedRulesMap.set(rule.id, rule);
359
+ nonAlwaysMatchedTitles.add(rule.title.toLowerCase());
360
+ }
361
+ } else {
362
+ unindexedKeywords.push(qk);
363
+ }
364
+ }
365
+ if (unindexedKeywords.length > 0) {
366
+ const searchPool = this.getCompiledSearchRules();
367
+ for (const entry of searchPool) {
368
+ if (targetDomain && entry.domainLower !== targetDomain)
369
+ continue;
370
+ if (matchedRulesMap.has(entry.rule.id))
371
+ continue;
372
+ for (const qk of unindexedKeywords) {
373
+ if (entry.searchableText.includes(qk)) {
374
+ matchedRulesMap.set(entry.rule.id, entry.rule);
375
+ nonAlwaysMatchedTitles.add(entry.rule.title.toLowerCase());
376
+ break;
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ const activeRelationsSet = new Set;
383
+ const visitedTitles = new Set(Array.from(matchedRulesMap.values()).map((r) => r.title.toLowerCase()));
384
+ let currentFrontier = new Set(nonAlwaysMatchedTitles.size > 0 ? nonAlwaysMatchedTitles : visitedTitles);
385
+ const effectiveHops = propagate ? Math.max(1, maxHops) : 1;
386
+ for (let hop = 0;hop < effectiveHops; hop++) {
387
+ const nextFrontier = new Set;
388
+ for (const titleLower of currentFrontier) {
389
+ const adjRelations = this.relationAdjacency.get(titleLower);
390
+ if (!adjRelations)
391
+ continue;
392
+ for (const rel of adjRelations) {
393
+ activeRelationsSet.add(rel);
394
+ if (propagate && nonAlwaysMatchedTitles.size > 0) {
395
+ const neighborLower = rel.from.toLowerCase() === titleLower ? rel.to.toLowerCase() : rel.from.toLowerCase();
396
+ if (!visitedTitles.has(neighborLower)) {
397
+ visitedTitles.add(neighborLower);
398
+ nextFrontier.add(neighborLower);
399
+ const neighborRule = this.titleToRuleMap.get(neighborLower);
400
+ if (neighborRule && (!targetDomain || neighborRule.domain.toLowerCase() === targetDomain)) {
401
+ matchedRulesMap.set(neighborRule.id, neighborRule);
402
+ }
403
+ }
404
+ }
405
+ }
406
+ }
407
+ if (nextFrontier.size === 0)
408
+ break;
409
+ currentFrontier = nextFrontier;
410
+ }
411
+ const matchedRules = Array.from(matchedRulesMap.values());
412
+ const activeRelations = Array.from(activeRelationsSet);
413
+ const duration = performance.now() - startTime;
414
+ const totalCount = this.database.rules.length;
415
+ const savings = totalCount > 0 ? ((totalCount - matchedRules.length) / totalCount * 100).toFixed(1) + "%" : "0.0%";
416
+ return {
417
+ queryDurationMs: Math.round(duration * 100) / 100,
418
+ matchedCount: matchedRules.length,
419
+ totalDatabaseRules: totalCount,
420
+ resonanceRules: matchedRules,
421
+ activeRelations,
422
+ tokenDietSavingsRatio: savings
423
+ };
424
+ }
425
+ assertRule(rule, persistToDisk = true) {
426
+ const existingIndex = this.database.rules.findIndex((r) => r.id === rule.id || r.title.toLowerCase() === rule.title.toLowerCase());
427
+ const updatedRules = [...this.database.rules];
428
+ const today = new Date().toISOString().split("T")[0];
429
+ if (existingIndex >= 0) {
430
+ updatedRules[existingIndex] = {
431
+ ...rule,
432
+ id: rule.id || updatedRules[existingIndex].id,
433
+ lastVerified: today
434
+ };
435
+ } else {
436
+ updatedRules.push({
437
+ ...rule,
438
+ lastVerified: today
439
+ });
440
+ }
441
+ this.database = {
442
+ ...this.database,
443
+ updatedAt: new Date().toISOString(),
444
+ totalRules: updatedRules.length,
445
+ rules: updatedRules
446
+ };
447
+ this.rebuildIndices();
448
+ if (persistToDisk) {
449
+ this.saveDatabase();
450
+ }
451
+ }
452
+ readSmartGraph(files = []) {
453
+ const resonance = this.queryResonance({ files });
454
+ const status = this.getStatus();
455
+ const entities = resonance.resonanceRules.map((r) => ({
456
+ name: r.title,
457
+ entityType: r.entityType || r.domain,
458
+ domain: r.domain,
459
+ invariants: r.invariants,
460
+ actions: r.actions,
461
+ observations: [
462
+ ...r.invariants.map((i) => `[\uBD88\uBCC0\uADDC\uCE59] ${i}`),
463
+ ...r.actions.map((a) => `[\uC2E4\uD589\uC9C0\uCE68] ${a}`),
464
+ ...r.context
465
+ ]
466
+ }));
467
+ return {
468
+ causalMemoryEngine: `v${status.version} (Active Resonance Mode)`,
469
+ tokenDietSavings: resonance.tokenDietSavingsRatio,
470
+ queryDurationMs: resonance.queryDurationMs,
471
+ activeEntitiesCount: entities.length,
472
+ totalIndexedRules: status.totalRules,
473
+ totalIndexedRelations: status.totalRelations,
474
+ availableDomains: status.domainCounts,
475
+ 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.",
476
+ entities,
477
+ relations: resonance.activeRelations
478
+ };
479
+ }
480
+ searchNodes(query) {
481
+ const trimmed = query.trim();
482
+ if (!trimmed) {
483
+ return this.readSmartGraph();
484
+ }
485
+ const tokens = trimmed.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
486
+ const matchedRules = [];
487
+ const matchedTitles = new Set;
488
+ const searchPool = this.getCompiledSearchRules();
489
+ for (const entry of searchPool) {
490
+ const ruleIdLower = entry.rule.id.toLowerCase();
491
+ if (tokens.some((tok) => entry.searchableText.includes(tok) || ruleIdLower.includes(tok))) {
492
+ matchedRules.push(entry.rule);
493
+ matchedTitles.add(entry.rule.title.toLowerCase());
494
+ }
495
+ }
496
+ const relationsSet = new Set;
497
+ for (const titleLower of matchedTitles) {
498
+ const adj = this.relationAdjacency.get(titleLower);
499
+ if (adj) {
500
+ for (const r of adj)
501
+ relationsSet.add(r);
502
+ }
503
+ }
504
+ return {
505
+ entities: matchedRules.map((r) => ({
506
+ name: r.title,
507
+ entityType: r.entityType || r.domain,
508
+ observations: [...r.invariants, ...r.actions, ...r.context]
509
+ })),
510
+ relations: Array.from(relationsSet)
511
+ };
512
+ }
513
+ openNodes(names) {
514
+ const matchedRules = [];
515
+ const matchedTitles = new Set;
516
+ for (const name of names) {
517
+ const lower = name.toLowerCase();
518
+ const rule = this.titleToRuleMap.get(lower) || this.idToRuleMap.get(lower);
519
+ if (rule && !matchedTitles.has(rule.title.toLowerCase())) {
520
+ matchedRules.push(rule);
521
+ matchedTitles.add(rule.title.toLowerCase());
522
+ }
523
+ }
524
+ const relationsSet = new Set;
525
+ for (const titleLower of matchedTitles) {
526
+ const adj = this.relationAdjacency.get(titleLower);
527
+ if (adj) {
528
+ for (const r of adj)
529
+ relationsSet.add(r);
530
+ }
531
+ }
532
+ return {
533
+ entities: matchedRules.map((r) => ({
534
+ name: r.title,
535
+ entityType: r.entityType || r.domain,
536
+ observations: [...r.invariants, ...r.actions, ...r.context]
537
+ })),
538
+ relations: Array.from(relationsSet)
539
+ };
540
+ }
541
+ createEntities(entities, persistToDisk = true) {
542
+ const created = [];
543
+ const updatedRules = [...this.database.rules];
544
+ const today = new Date().toISOString().split("T")[0];
545
+ for (const ent of entities) {
546
+ if (this.titleToRuleMap.has(ent.name.toLowerCase()))
547
+ continue;
548
+ const inferred = CausalMemoryStore.inferRuleFromEntity(ent, today);
549
+ updatedRules.push(inferred);
550
+ created.push(ent);
551
+ }
552
+ if (created.length > 0) {
553
+ this.database = {
554
+ ...this.database,
555
+ updatedAt: new Date().toISOString(),
556
+ totalRules: updatedRules.length,
557
+ rules: updatedRules
558
+ };
559
+ this.rebuildIndices();
560
+ if (persistToDisk) {
561
+ this.saveDatabase();
562
+ }
563
+ }
564
+ return created;
565
+ }
566
+ addObservations(observations, persistToDisk = true) {
567
+ const updatedRules = [...this.database.rules];
568
+ const results = [];
569
+ let changed = false;
570
+ for (const item of observations) {
571
+ const idx = updatedRules.findIndex((r) => r.title.toLowerCase() === item.entityName.toLowerCase());
572
+ if (idx < 0)
573
+ continue;
574
+ const rule = updatedRules[idx];
575
+ const existingObs = new Set([...rule.invariants, ...rule.actions, ...rule.context]);
576
+ const added = [];
577
+ const newInvariants = [...rule.invariants];
578
+ const newActions = [...rule.actions];
579
+ const newContext = [...rule.context];
580
+ for (const content of item.contents) {
581
+ if (existingObs.has(content))
582
+ continue;
583
+ added.push(content);
584
+ existingObs.add(content);
585
+ changed = true;
586
+ if (content.includes("\uAE08\uC9C0") || content.includes("\uC808\uB300") || content.includes("\uD544\uC218") || content.includes("\uC8FC\uC758")) {
587
+ newInvariants.push(content);
588
+ } else if (/^\d+\.|\uB2E8\uACC4|Step/i.test(content)) {
589
+ newActions.push(content);
590
+ } else {
591
+ newContext.push(content);
592
+ }
593
+ }
594
+ updatedRules[idx] = {
595
+ ...rule,
596
+ invariants: newInvariants,
597
+ actions: newActions,
598
+ context: newContext,
599
+ lastVerified: new Date().toISOString().split("T")[0]
600
+ };
601
+ results.push({ entityName: item.entityName, addedObservations: added });
602
+ }
603
+ if (changed) {
604
+ this.database = {
605
+ ...this.database,
606
+ updatedAt: new Date().toISOString(),
607
+ rules: updatedRules
608
+ };
609
+ this.rebuildIndices();
610
+ if (persistToDisk) {
611
+ this.saveDatabase();
612
+ }
613
+ }
614
+ return results;
615
+ }
616
+ createRelations(relations, persistToDisk = true) {
617
+ const updatedRelations = [...this.database.relations];
618
+ const created = [];
619
+ for (const rel of relations) {
620
+ const exists = updatedRelations.some((r) => r.from === rel.from && r.to === rel.to && r.relationType === rel.relationType);
621
+ if (!exists) {
622
+ updatedRelations.push(rel);
623
+ created.push(rel);
624
+ }
625
+ }
626
+ if (created.length > 0) {
627
+ this.database = {
628
+ ...this.database,
629
+ updatedAt: new Date().toISOString(),
630
+ totalRelations: updatedRelations.length,
631
+ relations: updatedRelations
632
+ };
633
+ this.rebuildIndices();
634
+ if (persistToDisk) {
635
+ this.saveDatabase();
636
+ }
637
+ }
638
+ return created;
639
+ }
640
+ deleteEntities(entityNames, persistToDisk = true) {
641
+ const nameSet = new Set(entityNames.map((n) => n.toLowerCase()));
642
+ const filteredRules = this.database.rules.filter((r) => !nameSet.has(r.title.toLowerCase()) && !nameSet.has(r.id.toLowerCase()));
643
+ const filteredRelations = this.database.relations.filter((rel) => !nameSet.has(rel.from.toLowerCase()) && !nameSet.has(rel.to.toLowerCase()));
644
+ this.database = {
645
+ ...this.database,
646
+ updatedAt: new Date().toISOString(),
647
+ totalRules: filteredRules.length,
648
+ totalRelations: filteredRelations.length,
649
+ rules: filteredRules,
650
+ relations: filteredRelations
651
+ };
652
+ this.rebuildIndices();
653
+ if (persistToDisk) {
654
+ this.saveDatabase();
655
+ }
656
+ }
657
+ deleteObservations(deletions, persistToDisk = true) {
658
+ const updatedRules = [...this.database.rules];
659
+ for (const del of deletions) {
660
+ const idx = updatedRules.findIndex((r) => r.title.toLowerCase() === del.entityName.toLowerCase());
661
+ if (idx < 0)
662
+ continue;
663
+ const toDelete = new Set(del.observations);
664
+ const rule = updatedRules[idx];
665
+ updatedRules[idx] = {
666
+ ...rule,
667
+ invariants: rule.invariants.filter((x) => !toDelete.has(x)),
668
+ actions: rule.actions.filter((x) => !toDelete.has(x)),
669
+ context: rule.context.filter((x) => !toDelete.has(x))
670
+ };
671
+ }
672
+ this.database = {
673
+ ...this.database,
674
+ updatedAt: new Date().toISOString(),
675
+ rules: updatedRules
676
+ };
677
+ this.rebuildIndices();
678
+ if (persistToDisk) {
679
+ this.saveDatabase();
680
+ }
681
+ }
682
+ deleteRelations(relations, persistToDisk = true) {
683
+ const filtered = this.database.relations.filter((existing) => !relations.some((del) => del.from === existing.from && del.to === existing.to && del.relationType === existing.relationType));
684
+ this.database = {
685
+ ...this.database,
686
+ updatedAt: new Date().toISOString(),
687
+ totalRelations: filtered.length,
688
+ relations: filtered
689
+ };
690
+ this.rebuildIndices();
691
+ if (persistToDisk) {
692
+ this.saveDatabase();
693
+ }
694
+ }
695
+ static inferRuleFromEntity(ent, today) {
696
+ const nameLower = ent.name.toLowerCase();
697
+ const typeLower = ent.entityType.toLowerCase();
698
+ const obsJoined = ent.observations.join(" ").toLowerCase();
699
+ let domain = "Knowledge_Base";
700
+ const triggers = {};
701
+ const invariants = [];
702
+ const actions = [];
703
+ const context = [];
704
+ const isTrueUserPreference = typeLower === "preference" || typeLower === "user_constraint" || typeLower === "forbidden_rules" || nameLower === "userpreferences" || nameLower === "user_web_navigation_preference" || nameLower === "python_strictly_forbidden" || nameLower === "forbidden_topics_until_mentioned";
705
+ if (isTrueUserPreference) {
706
+ domain = "User_Preferences_And_Rules";
707
+ triggers.alwaysActive = true;
708
+ triggers.keywords = ["\uD658\uACBD\uC124\uC815", "\uADDC\uCE59", "\uD130\uBBF8\uB110", "\uCD9C\uB825", "\uBCF4\uC548", "\uC120\uD638\uB3C4"];
709
+ } else if (nameLower.includes("poe2") || nameLower.includes("noble-babbage") || nameLower.includes("release_workflow") || obsJoined.includes("poe2filter-releases") || obsJoined.includes("noble-babbage")) {
710
+ domain = "Project_POE2_AutoFilter";
711
+ triggers.files = [
712
+ "noble-babbage",
713
+ "poe2filter-releases",
714
+ "updater.json",
715
+ "post_download_button.js"
716
+ ];
717
+ triggers.keywords = ["poe2", "autofilter", "\uD544\uD130", "\uC624\uD1A0\uD544\uD130", "updater", "discord", "\uB9B4\uB9AC\uC988", "\uBC30\uD3EC"];
718
+ triggers.commands = ["release", "updater", "build", "sign"];
719
+ } else if (nameLower.includes("vesper") || nameLower.includes("dsp") || nameLower.includes("neurospatial audio") || typeLower.includes("audio_architecture")) {
720
+ domain = "Project_Vesper_DSP";
721
+ triggers.files = ["vesper", "crates/vesper-", "vesper-apo"];
722
+ triggers.keywords = ["vesper", "dsp", "\uC624\uB514\uC624", "audio", "mmcss", "virtuallock", "apo", "eq", "\uC0AC\uC6B4\uB4DC"];
723
+ triggers.commands = ["check", "build", "test"];
724
+ } else if (nameLower.includes("synapse") && !nameLower.includes("biophys")) {
725
+ domain = "Project_Synapse_Kit";
726
+ triggers.files = ["synapse", "synapse.config.ts"];
727
+ triggers.keywords = ["synapse", "synapse-kit", "\uC2DC\uB0C5\uC2A4", "\uC778\uACFC\uC728", "causal", "\uCEF4\uD30C\uC77C\uB7EC", "\uCCB4\uCEE4"];
728
+ triggers.commands = ["check", "build", "package", "sign", "init", "memory"];
729
+ } else if (nameLower.includes("hardware") || nameLower.includes("pcb") || nameLower.includes("jlcpcb") || nameLower.includes("m2_2230") || nameLower.includes("pocket_orange") || typeLower.includes("hardware")) {
730
+ domain = "Hardware_And_PCB";
731
+ triggers.files = ["hardware", "pcb", "kicad"];
732
+ triggers.keywords = ["pcb", "hardware", "\uD558\uB4DC\uC6E8\uC5B4", "\uD68C\uB85C", "jlcpcb", "kicad", "m2"];
733
+ } else if (nameLower.includes("biophys") || nameLower.includes("connectome") || nameLower.includes("drosophila") || nameLower.includes("cortex") || typeLower.includes("whitepaper")) {
734
+ domain = "BioPhys_Research_And_Architecture";
735
+ triggers.files = ["biophys", "whitepaper"];
736
+ triggers.keywords = ["biophys", "connectome", "\uC2DC\uB0C5\uC2A4\uC2E0\uACBD\uB9DD", "\uD000\uD0C0\uC774\uC81C\uC774\uC158", "\uBC31\uC11C", "\uC544\uD0A4\uD14D\uCC98"];
737
+ } else {
738
+ domain = "Knowledge_Base";
739
+ triggers.keywords = [ent.name.toLowerCase().replace(/_/g, " ")];
740
+ }
741
+ for (const obs of ent.observations) {
742
+ if (obs.includes("\uAE08\uC9C0") || obs.includes("\uC808\uB300") || obs.includes("\uD544\uC218") || obs.includes("\uC8FC\uC758") || obs.includes("VirtualLock") || obs.includes("MMCSS")) {
743
+ invariants.push(obs);
744
+ } else if (/^\d+\.|\uB2E8\uACC4|Step/i.test(obs)) {
745
+ actions.push(obs);
746
+ } else {
747
+ context.push(obs);
748
+ }
749
+ }
750
+ const baseSlug = ent.name.trim().toLowerCase().replace(/[^\w\uAC00-\uD7A30-9-]+/g, "-").replace(/^-+|-+$/g, "") || "item";
751
+ return {
752
+ id: `rule-${domain.toLowerCase()}-${baseSlug}`,
753
+ domain,
754
+ title: ent.name,
755
+ entityType: ent.entityType,
756
+ triggers,
757
+ invariants,
758
+ actions,
759
+ context,
760
+ lastVerified: today
761
+ };
762
+ }
763
+ audit() {
764
+ const issues = [];
765
+ const seenIds = new Set;
766
+ let alwaysActiveCount = 0;
767
+ for (const rule of this.database.rules) {
768
+ if (rule.triggers.alwaysActive) {
769
+ alwaysActiveCount++;
770
+ }
771
+ if (!rule.id || rule.id.trim().length === 0) {
772
+ issues.push(`ID \uB204\uB77D \uADDC\uCE59 \uBC1C\uACAC: ${rule.title}`);
773
+ } else if (seenIds.has(rule.id)) {
774
+ issues.push(`\uC911\uBCF5 ID \uBC1C\uACAC: ${rule.id}`);
775
+ } else {
776
+ seenIds.add(rule.id);
777
+ }
778
+ if (!rule.domain) {
779
+ issues.push(`\uB3C4\uBA54\uC778 \uB204\uB77D \uADDC\uCE59: ${rule.id}`);
780
+ }
781
+ }
782
+ if (alwaysActiveCount > 10) {
783
+ 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.`);
784
+ }
785
+ return {
786
+ ok: issues.length === 0,
787
+ totalRules: this.database.rules.length,
788
+ totalRelations: this.database.relations.length,
789
+ alwaysActiveCount,
790
+ issues
791
+ };
792
+ }
793
+ }
794
+ var init_causal_store = __esm(() => {
795
+ init_causal_config();
796
+ });
797
+
798
+ // causal-query.ts
799
+ var {spawn } = globalThis.Bun;
800
+ async function detectModifiedFiles(cwd = process.cwd()) {
801
+ try {
802
+ const gitProc = spawn(["git", "status", "--porcelain", "-uall"], {
803
+ cwd,
804
+ stdout: "pipe",
805
+ stderr: "pipe"
806
+ });
807
+ const output = await new Response(gitProc.stdout).text();
808
+ await gitProc.exited;
809
+ return output.split(`
810
+ `).map((line) => line.slice(3).trim().replaceAll("\\", "/")).filter((line) => line.length > 0 && !line.startsWith(".synapse/") && !line.includes("/.synapse/"));
811
+ } catch {
812
+ return [];
813
+ }
814
+ }
815
+ async function runCausalQuery(rawOptions, storeCustomPath) {
816
+ const store = new CausalMemoryStore(storeCustomPath);
817
+ let targetFiles = rawOptions.files ? [...rawOptions.files] : [];
818
+ if (rawOptions.autoGit || targetFiles.length === 0) {
819
+ const gitFiles = await detectModifiedFiles(rawOptions.cwd || process.cwd());
820
+ if (gitFiles.length > 0) {
821
+ targetFiles = Array.from(new Set([...targetFiles, ...gitFiles]));
822
+ }
823
+ }
824
+ const queryOptions = {
825
+ files: targetFiles,
826
+ keywords: rawOptions.keywords,
827
+ command: rawOptions.command,
828
+ domain: rawOptions.domain,
829
+ propagateRelations: rawOptions.propagateRelations
830
+ };
831
+ return store.queryResonance(queryOptions);
832
+ }
833
+ function formatResonanceForAgent(result, compact = false) {
834
+ if (result.matchedCount === 0) {
835
+ return `[Causal-Memory] \uACF5\uBA85 \uADDC\uCE59 0\uAC74 (\uD1A0\uD070 \uC808\uAC10\uB960: ${result.tokenDietSavingsRatio}, \uC18C\uC694: ${result.queryDurationMs}ms)`;
836
+ }
837
+ if (compact) {
838
+ return JSON.stringify({
839
+ ok: true,
840
+ durationMs: result.queryDurationMs,
841
+ matchedCount: result.matchedCount,
842
+ totalDatabaseRules: result.totalDatabaseRules,
843
+ savings: result.tokenDietSavingsRatio,
844
+ rules: result.resonanceRules.map((r) => ({
845
+ id: r.id,
846
+ domain: r.domain,
847
+ title: r.title,
848
+ invariants: r.invariants,
849
+ actions: r.actions
850
+ })),
851
+ relations: result.activeRelations
852
+ });
853
+ }
854
+ const lines = [
855
+ `### \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)`
856
+ ];
857
+ for (const rule of result.resonanceRules) {
858
+ lines.push(`
859
+ **[${rule.domain}] ${rule.title}**`);
860
+ if (rule.invariants.length > 0) {
861
+ lines.push(" - **\uBD88\uBCC0 \uADDC\uCE59(Invariants)**:");
862
+ for (const inv of rule.invariants) {
863
+ lines.push(` \u2022 ${inv}`);
864
+ }
865
+ }
866
+ if (rule.actions.length > 0) {
867
+ lines.push(" - **\uC2E4\uD589 \uC808\uCC28(Actions)**:");
868
+ for (const act of rule.actions) {
869
+ lines.push(` \u2022 ${act}`);
870
+ }
871
+ }
872
+ if (rule.context.length > 0) {
873
+ lines.push(" - **\uD575\uC2EC \uB9E5\uB77D(Context)**:");
874
+ for (const ctx of rule.context.slice(0, CAUSAL_MEMORY_CONFIG.MAX_CONTEXT_PREVIEW_LINES)) {
875
+ lines.push(` \u2022 ${ctx}`);
876
+ }
877
+ }
878
+ }
879
+ if (result.activeRelations.length > 0) {
880
+ lines.push(`
881
+ ### \uD83D\uDD17 \uD65C\uC131 \uC778\uACFC \uAC04\uC120 (Active Causal Relations)`);
882
+ for (const rel of result.activeRelations) {
883
+ lines.push(` \u2022 \`${rel.from}\` \u2500\u2500[${rel.relationType}]\u2500\u2500\u25B6 \`${rel.to}\``);
884
+ }
885
+ }
886
+ return lines.join(`
887
+ `);
888
+ }
889
+ var init_causal_query = __esm(() => {
890
+ init_causal_config();
891
+ init_causal_store();
892
+ });
893
+
894
+ // causal-server.ts
895
+ import { createInterface } from "readline";
896
+ async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
897
+ const { id, method, params } = rpc;
898
+ if (method.startsWith("notifications/")) {
899
+ return null;
900
+ }
901
+ if (method === "initialize") {
902
+ return {
903
+ jsonrpc: "2.0",
904
+ id: id ?? null,
905
+ result: {
906
+ protocolVersion: params?.protocolVersion || CAUSAL_MEMORY_CONFIG.MCP_PROTOCOL_VERSION,
907
+ capabilities: {
908
+ tools: {}
909
+ },
910
+ serverInfo: {
911
+ name: CAUSAL_MEMORY_CONFIG.MCP_SERVER_NAME,
912
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION
913
+ }
914
+ }
915
+ };
916
+ }
917
+ if (method === "ping") {
918
+ return {
919
+ jsonrpc: "2.0",
920
+ id: id ?? null,
921
+ result: {}
922
+ };
923
+ }
924
+ if (method === "tools/list") {
925
+ return {
926
+ jsonrpc: "2.0",
927
+ id: id ?? null,
928
+ result: {
929
+ tools: CAUSAL_MCP_TOOLS
930
+ }
931
+ };
932
+ }
933
+ if (method === "tools/call") {
934
+ store.reloadIfNeeded();
935
+ const toolName = params?.name;
936
+ const args = params?.arguments || {};
937
+ try {
938
+ let payloadText = "";
939
+ switch (toolName) {
940
+ case "query_causal_memory": {
941
+ let files = Array.isArray(args.files) ? args.files : [];
942
+ if (files.length === 0 && args.autoGit !== false) {
943
+ files = await detectModifiedFiles();
944
+ }
945
+ const res = store.queryResonance({
946
+ files,
947
+ keywords: args.keywords,
948
+ command: args.command,
949
+ domain: args.domain,
950
+ propagateRelations: args.propagateRelations,
951
+ maxHops: args.maxHops
952
+ });
953
+ payloadText = formatResonanceForAgent(res, Boolean(args.compact));
954
+ break;
955
+ }
956
+ case "assert_causal_rule": {
957
+ store.assertRule(args);
958
+ payloadText = JSON.stringify({
959
+ ok: true,
960
+ message: `\uC778\uACFC\uC728 \uADDC\uCE59 \uB4F1\uB85D/\uAC31\uC2E0 \uC644\uB8CC: ${args.id}`,
961
+ id: args.id
962
+ });
963
+ break;
964
+ }
965
+ case "audit_causal_memory": {
966
+ const audit = store.audit();
967
+ const status = store.getStatus();
968
+ payloadText = JSON.stringify({ ...audit, status }, null, 2);
969
+ break;
970
+ }
971
+ case "read_graph": {
972
+ const gitFiles = await detectModifiedFiles();
973
+ const smartGraph = store.readSmartGraph(gitFiles);
974
+ payloadText = JSON.stringify(smartGraph, null, 2);
975
+ break;
976
+ }
977
+ case "search_nodes": {
978
+ const searchResult = store.searchNodes(String(args.query || ""));
979
+ payloadText = JSON.stringify(searchResult, null, 2);
980
+ break;
981
+ }
982
+ case "open_nodes": {
983
+ const names = Array.isArray(args.names) ? args.names : [];
984
+ const openResult = store.openNodes(names);
985
+ payloadText = JSON.stringify(openResult, null, 2);
986
+ break;
987
+ }
988
+ case "create_entities": {
989
+ const entities = Array.isArray(args.entities) ? args.entities : [];
990
+ const created = store.createEntities(entities);
991
+ payloadText = JSON.stringify(created, null, 2);
992
+ break;
993
+ }
994
+ case "create_relations": {
995
+ const relations = Array.isArray(args.relations) ? args.relations : [];
996
+ const created = store.createRelations(relations);
997
+ payloadText = JSON.stringify(created, null, 2);
998
+ break;
999
+ }
1000
+ case "add_observations": {
1001
+ const observations = Array.isArray(args.observations) ? args.observations : [];
1002
+ const added = store.addObservations(observations);
1003
+ payloadText = JSON.stringify(added, null, 2);
1004
+ break;
1005
+ }
1006
+ case "delete_entities": {
1007
+ const entityNames = Array.isArray(args.entityNames) ? args.entityNames : [];
1008
+ store.deleteEntities(entityNames);
1009
+ payloadText = "Entities deleted successfully";
1010
+ break;
1011
+ }
1012
+ case "delete_observations": {
1013
+ const deletions = Array.isArray(args.deletions) ? args.deletions : [];
1014
+ store.deleteObservations(deletions);
1015
+ payloadText = "Observations deleted successfully";
1016
+ break;
1017
+ }
1018
+ case "delete_relations": {
1019
+ const relations = Array.isArray(args.relations) ? args.relations : [];
1020
+ store.deleteRelations(relations);
1021
+ payloadText = "Relations deleted successfully";
1022
+ break;
1023
+ }
1024
+ default:
1025
+ return {
1026
+ jsonrpc: "2.0",
1027
+ id: id ?? null,
1028
+ error: {
1029
+ code: -32601,
1030
+ message: `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB3C4\uAD6C\uC785\uB2C8\uB2E4: ${toolName}`
1031
+ }
1032
+ };
1033
+ }
1034
+ return {
1035
+ jsonrpc: "2.0",
1036
+ id: id ?? null,
1037
+ result: {
1038
+ content: [{ type: "text", text: payloadText }]
1039
+ }
1040
+ };
1041
+ } catch (error) {
1042
+ return {
1043
+ jsonrpc: "2.0",
1044
+ id: id ?? null,
1045
+ result: {
1046
+ content: [
1047
+ {
1048
+ type: "text",
1049
+ text: `\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB3C4\uAD6C \uC2E4\uD589 \uC624\uB958: ${error instanceof Error ? error.message : String(error)}`
1050
+ }
1051
+ ],
1052
+ isError: true
1053
+ }
1054
+ };
1055
+ }
1056
+ }
1057
+ return {
1058
+ jsonrpc: "2.0",
1059
+ id: id ?? null,
1060
+ error: { code: -32601, message: `Method not found: ${method}` }
1061
+ };
1062
+ }
1063
+ function startCausalServer(options = {}) {
1064
+ const port = options.port || CAUSAL_MEMORY_CONFIG.DEFAULT_PORT;
1065
+ const host = options.host || CAUSAL_MEMORY_CONFIG.DEFAULT_HOST;
1066
+ const store = new CausalMemoryStore(options.dbPath);
1067
+ const server = Bun.serve({
1068
+ port,
1069
+ hostname: host,
1070
+ async fetch(req) {
1071
+ const url = new URL(req.url);
1072
+ const headers = {
1073
+ "Content-Type": "application/json",
1074
+ "Access-Control-Allow-Origin": "*",
1075
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
1076
+ "Access-Control-Allow-Headers": "Content-Type"
1077
+ };
1078
+ if (req.method === "OPTIONS") {
1079
+ return new Response(null, { headers });
1080
+ }
1081
+ try {
1082
+ if (url.pathname === "/health" || url.pathname === "/status") {
1083
+ store.reloadIfNeeded();
1084
+ return new Response(JSON.stringify({ ok: true, status: store.getStatus() }), { headers });
1085
+ }
1086
+ if (url.pathname === "/query" && req.method === "GET") {
1087
+ store.reloadIfNeeded();
1088
+ const files = url.searchParams.getAll("files");
1089
+ const keywords = url.searchParams.getAll("keywords");
1090
+ const command = url.searchParams.get("command") || undefined;
1091
+ const domain = url.searchParams.get("domain") || undefined;
1092
+ const result = store.queryResonance({ files, keywords, command, domain });
1093
+ return new Response(JSON.stringify(result), { headers });
1094
+ }
1095
+ if (url.pathname === "/query" && req.method === "POST") {
1096
+ store.reloadIfNeeded();
1097
+ const body = await req.json();
1098
+ let files = Array.isArray(body.files) ? body.files : [];
1099
+ if (body.autoGit === true && files.length === 0) {
1100
+ files = await detectModifiedFiles(body.cwd);
1101
+ }
1102
+ const result = store.queryResonance({
1103
+ files,
1104
+ keywords: body.keywords,
1105
+ command: body.command,
1106
+ domain: body.domain,
1107
+ propagateRelations: body.propagateRelations,
1108
+ maxHops: body.maxHops
1109
+ });
1110
+ const format = url.searchParams.get("format") || body.format;
1111
+ if (format === "markdown" || format === "md") {
1112
+ return new Response(formatResonanceForAgent(result, false), {
1113
+ headers: { ...headers, "Content-Type": "text/markdown; charset=utf-8" }
1114
+ });
1115
+ }
1116
+ if (format === "compact") {
1117
+ return new Response(formatResonanceForAgent(result, true), { headers });
1118
+ }
1119
+ return new Response(JSON.stringify(result), { headers });
1120
+ }
1121
+ if (url.pathname === "/assert" && req.method === "POST") {
1122
+ store.reloadIfNeeded();
1123
+ const body = await req.json();
1124
+ store.assertRule(body);
1125
+ return new Response(JSON.stringify({ ok: true, id: body.id }), { headers });
1126
+ }
1127
+ if (url.pathname === "/audit") {
1128
+ store.reloadIfNeeded();
1129
+ const auditResult = store.audit();
1130
+ return new Response(JSON.stringify(auditResult), { headers });
1131
+ }
1132
+ if (url.pathname === "/mcp" && req.method === "POST") {
1133
+ const rpc = await req.json();
1134
+ const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath);
1135
+ if (!responseObj) {
1136
+ return new Response(null, { status: 204, headers });
1137
+ }
1138
+ return new Response(JSON.stringify(responseObj), { headers });
1139
+ }
1140
+ return new Response(JSON.stringify({ error: "Not Found" }), { status: 404, headers });
1141
+ } catch (err) {
1142
+ return new Response(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }), { status: 500, headers });
1143
+ }
1144
+ }
1145
+ });
1146
+ return server;
1147
+ }
1148
+ async function startCausalMcpStdioServer(dbPath) {
1149
+ const store = new CausalMemoryStore(dbPath);
1150
+ const daemonUrl = `http://${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST}:${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT}/mcp`;
1151
+ const rl = createInterface({
1152
+ input: process.stdin,
1153
+ terminal: false
1154
+ });
1155
+ for await (const line of rl) {
1156
+ const trimmed = line.trim();
1157
+ if (!trimmed)
1158
+ continue;
1159
+ try {
1160
+ const rpc = JSON.parse(trimmed);
1161
+ let responseObj = null;
1162
+ if (rpc.method === "tools/call") {
1163
+ try {
1164
+ const daemonRes = await fetch(daemonUrl, {
1165
+ method: "POST",
1166
+ headers: { "Content-Type": "application/json" },
1167
+ body: trimmed,
1168
+ signal: AbortSignal.timeout(150)
1169
+ });
1170
+ if (daemonRes.ok) {
1171
+ responseObj = await daemonRes.json();
1172
+ }
1173
+ } catch {}
1174
+ }
1175
+ if (!responseObj) {
1176
+ responseObj = await handleMcpJsonRpcRequest(rpc, store, dbPath);
1177
+ }
1178
+ if (responseObj) {
1179
+ process.stdout.write(JSON.stringify(responseObj) + `
1180
+ `);
1181
+ }
1182
+ } catch (error) {
1183
+ const errResp = {
1184
+ jsonrpc: "2.0",
1185
+ id: null,
1186
+ error: {
1187
+ code: -32700,
1188
+ message: `JSON Parse error: ${error instanceof Error ? error.message : String(error)}`
1189
+ }
1190
+ };
1191
+ process.stdout.write(JSON.stringify(errResp) + `
1192
+ `);
1193
+ }
1194
+ }
1195
+ }
1196
+ var CAUSAL_MCP_TOOLS;
1197
+ var init_causal_server = __esm(() => {
1198
+ init_causal_config();
1199
+ init_causal_store();
1200
+ init_causal_query();
1201
+ CAUSAL_MCP_TOOLS = [
1202
+ {
1203
+ name: "query_causal_memory",
1204
+ 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.",
1205
+ inputSchema: {
1206
+ type: "object",
1207
+ properties: {
1208
+ files: {
1209
+ type: "array",
1210
+ items: { type: "string" },
1211
+ description: "\uAC10\uC9C0\uD560 \uB300\uC0C1 \uD30C\uC77C \uACBD\uB85C \uBAA9\uB85D (\uC0DD\uB7B5 \uC2DC Git \uBCC0\uACBD \uD30C\uC77C \uC790\uB3D9 \uAC10\uC9C0)"
1212
+ },
1213
+ keywords: {
1214
+ type: "array",
1215
+ items: { type: "string" },
1216
+ description: "\uAC80\uC0C9\uD560 \uB3C4\uBA54\uC778/\uC791\uC5C5 \uD0A4\uC6CC\uB4DC \uBAA9\uB85D"
1217
+ },
1218
+ command: {
1219
+ type: "string",
1220
+ description: "\uC5F0\uB3D9 \uBA85\uB839\uC5B4 (\uC608: check, build, release, sign)"
1221
+ },
1222
+ domain: {
1223
+ type: "string",
1224
+ description: "\uD2B9\uC815 \uB3C4\uBA54\uC778 \uD544\uD130"
1225
+ },
1226
+ compact: {
1227
+ type: "boolean",
1228
+ description: "\uCD08\uC18C\uD615 JSON \uD3EC\uB9F7 \uBC18\uD658 \uC5EC\uBD80"
1229
+ }
1230
+ }
1231
+ }
1232
+ },
1233
+ {
1234
+ name: "assert_causal_rule",
1235
+ 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.",
1236
+ inputSchema: {
1237
+ type: "object",
1238
+ required: ["id", "domain", "title", "triggers"],
1239
+ properties: {
1240
+ id: { type: "string" },
1241
+ domain: { type: "string" },
1242
+ title: { type: "string" },
1243
+ triggers: { type: "object" },
1244
+ invariants: { type: "array", items: { type: "string" } },
1245
+ actions: { type: "array", items: { type: "string" } },
1246
+ context: { type: "array", items: { type: "string" } }
1247
+ }
1248
+ }
1249
+ },
1250
+ {
1251
+ name: "audit_causal_memory",
1252
+ 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.",
1253
+ inputSchema: {
1254
+ type: "object",
1255
+ properties: {}
1256
+ }
1257
+ },
1258
+ {
1259
+ name: "read_graph",
1260
+ description: "Read the active causal resonance knowledge graph (automatically filters out unrelated noise for 90%+ token savings)",
1261
+ inputSchema: {
1262
+ type: "object",
1263
+ properties: {}
1264
+ }
1265
+ },
1266
+ {
1267
+ name: "search_nodes",
1268
+ description: "Search for causal rules and nodes in the knowledge graph based on a query and traverse causal relations",
1269
+ inputSchema: {
1270
+ type: "object",
1271
+ required: ["query"],
1272
+ properties: {
1273
+ query: {
1274
+ type: "string",
1275
+ description: "The search query to match against rule titles, domains, triggers, and invariants"
1276
+ }
1277
+ }
1278
+ }
1279
+ },
1280
+ {
1281
+ name: "open_nodes",
1282
+ description: "Open specific causal rule nodes in the knowledge graph by their names or IDs",
1283
+ inputSchema: {
1284
+ type: "object",
1285
+ required: ["names"],
1286
+ properties: {
1287
+ names: {
1288
+ type: "array",
1289
+ items: { type: "string" },
1290
+ description: "An array of entity names or rule IDs to retrieve"
1291
+ }
1292
+ }
1293
+ }
1294
+ },
1295
+ {
1296
+ name: "create_entities",
1297
+ description: "Create multiple new entities (automatically converted into structured Causal Rules)",
1298
+ inputSchema: {
1299
+ type: "object",
1300
+ required: ["entities"],
1301
+ properties: {
1302
+ entities: {
1303
+ type: "array",
1304
+ items: {
1305
+ type: "object",
1306
+ required: ["name", "entityType", "observations"],
1307
+ properties: {
1308
+ name: { type: "string" },
1309
+ entityType: { type: "string" },
1310
+ observations: { type: "array", items: { type: "string" } }
1311
+ }
1312
+ }
1313
+ }
1314
+ }
1315
+ }
1316
+ },
1317
+ {
1318
+ name: "create_relations",
1319
+ description: "Create multiple new causal relations between nodes in the knowledge graph",
1320
+ inputSchema: {
1321
+ type: "object",
1322
+ required: ["relations"],
1323
+ properties: {
1324
+ relations: {
1325
+ type: "array",
1326
+ items: {
1327
+ type: "object",
1328
+ required: ["from", "to", "relationType"],
1329
+ properties: {
1330
+ from: { type: "string" },
1331
+ to: { type: "string" },
1332
+ relationType: { type: "string" }
1333
+ }
1334
+ }
1335
+ }
1336
+ }
1337
+ }
1338
+ },
1339
+ {
1340
+ name: "add_observations",
1341
+ description: "Add new invariants, actions, or context observations to existing causal rules",
1342
+ inputSchema: {
1343
+ type: "object",
1344
+ required: ["observations"],
1345
+ properties: {
1346
+ observations: {
1347
+ type: "array",
1348
+ items: {
1349
+ type: "object",
1350
+ required: ["entityName", "contents"],
1351
+ properties: {
1352
+ entityName: { type: "string" },
1353
+ contents: { type: "array", items: { type: "string" } }
1354
+ }
1355
+ }
1356
+ }
1357
+ }
1358
+ }
1359
+ },
1360
+ {
1361
+ name: "delete_entities",
1362
+ description: "Delete multiple entities/rules and their associated causal relations",
1363
+ inputSchema: {
1364
+ type: "object",
1365
+ required: ["entityNames"],
1366
+ properties: {
1367
+ entityNames: {
1368
+ type: "array",
1369
+ items: { type: "string" }
1370
+ }
1371
+ }
1372
+ }
1373
+ },
1374
+ {
1375
+ name: "delete_observations",
1376
+ description: "Delete specific observations from entities/rules in the causal memory",
1377
+ inputSchema: {
1378
+ type: "object",
1379
+ required: ["deletions"],
1380
+ properties: {
1381
+ deletions: {
1382
+ type: "array",
1383
+ items: {
1384
+ type: "object",
1385
+ required: ["entityName", "observations"],
1386
+ properties: {
1387
+ entityName: { type: "string" },
1388
+ observations: { type: "array", items: { type: "string" } }
1389
+ }
1390
+ }
1391
+ }
1392
+ }
1393
+ }
1394
+ },
1395
+ {
1396
+ name: "delete_relations",
1397
+ description: "Delete multiple causal relations from the knowledge graph",
1398
+ inputSchema: {
1399
+ type: "object",
1400
+ required: ["relations"],
1401
+ properties: {
1402
+ relations: {
1403
+ type: "array",
1404
+ items: {
1405
+ type: "object",
1406
+ required: ["from", "to", "relationType"],
1407
+ properties: {
1408
+ from: { type: "string" },
1409
+ to: { type: "string" },
1410
+ relationType: { type: "string" }
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ }
1416
+ }
1417
+ ];
1418
+ });
1419
+
1420
+ // causal-benchmark.ts
1421
+ function calcPercentile(sorted, p) {
1422
+ if (sorted.length === 0)
1423
+ return 0;
1424
+ const idx = Math.min(sorted.length - 1, Math.floor(p / 100 * sorted.length));
1425
+ return Math.round(sorted[idx] * 1000) / 1000;
1426
+ }
1427
+ function generateSyntheticDatabase(ruleCount, relationCount) {
1428
+ const domains = [
1429
+ "Project_POE2_AutoFilter",
1430
+ "Project_Vesper_DSP",
1431
+ "Project_Synapse_Kit",
1432
+ "BioPhys_Research_And_Architecture",
1433
+ "Hardware_And_PCB",
1434
+ "Knowledge_Base"
1435
+ ];
1436
+ const rules = [
1437
+ {
1438
+ id: "rule-always-1",
1439
+ domain: "User_Preferences_And_Rules",
1440
+ title: "Global_Preference_1",
1441
+ triggers: { alwaysActive: true },
1442
+ invariants: ["\uD56D\uC0C1 \uD55C\uAD6D\uC5B4 \uC6B0\uC120 \uCD9C\uB825", "bun \uD544\uC218 \uC0AC\uC6A9"],
1443
+ actions: [],
1444
+ context: []
1445
+ },
1446
+ {
1447
+ id: "rule-always-2",
1448
+ domain: "User_Preferences_And_Rules",
1449
+ title: "Global_Preference_2",
1450
+ triggers: { alwaysActive: true },
1451
+ invariants: ["console.log \uB514\uBC84\uADF8 \uCF54\uB4DC \uAE08\uC9C0", "\uD558\uB4DC\uCF54\uB529 \uAE08\uC9C0"],
1452
+ actions: [],
1453
+ context: []
1454
+ },
1455
+ {
1456
+ id: "rule-always-3",
1457
+ domain: "User_Preferences_And_Rules",
1458
+ title: "Global_Preference_3",
1459
+ triggers: { alwaysActive: true },
1460
+ invariants: ["100% \uC21C\uC218 Rust \uBC31\uC5D4\uB4DC \uC6D0\uCE59"],
1461
+ actions: [],
1462
+ context: []
1463
+ },
1464
+ {
1465
+ id: "rule-always-4",
1466
+ domain: "User_Preferences_And_Rules",
1467
+ title: "Global_Preference_4",
1468
+ triggers: { alwaysActive: true },
1469
+ invariants: ["\uAC80\uC99D \uCD5C\uC6B0\uC120 \uC6D0\uCE59 \uC900\uC218"],
1470
+ actions: [],
1471
+ context: []
1472
+ }
1473
+ ];
1474
+ for (let i = 4;i < ruleCount; i++) {
1475
+ const dom = domains[i % domains.length];
1476
+ const moduleIdx = i % 500;
1477
+ rules.push({
1478
+ id: `rule-synth-${i}`,
1479
+ domain: dom,
1480
+ title: `Synth_Node_${i}`,
1481
+ triggers: {
1482
+ files: [`packages/mod_${moduleIdx}/src/file_${i}.rs`],
1483
+ keywords: [`kw_${i}`, `topic_${i % 250}`],
1484
+ commands: i % 20 === 0 ? ["build"] : i % 20 === 1 ? ["release"] : ["check"]
1485
+ },
1486
+ invariants: [`\uBD88\uBCC0 \uACC4\uC57D #${i}: \uBA54\uBAA8\uB9AC \uC548\uC804\uC131 \uBC0F \uD0C0\uC785 \uACBD\uACC4 \uBCF4\uC7A5`],
1487
+ actions: [`1. \uBAA8\uB4C8 ${moduleIdx} \uC815\uC801 \uAC80\uC99D \uC218\uD589`],
1488
+ context: [`\uD569\uC131 \uBCA4\uCE58\uB9C8\uD06C \uB178\uB4DC \uCEE8\uD14D\uC2A4\uD2B8 \uB370\uC774\uD130 #${i}`],
1489
+ lastVerified: "2026-11-01"
1490
+ });
1491
+ }
1492
+ const relations = [];
1493
+ 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" });
1494
+ for (let j = relations.length;j < relationCount; j++) {
1495
+ const fromIdx = 4 + j % (ruleCount - 10);
1496
+ const toIdx = fromIdx + 1 + j % 5;
1497
+ relations.push({
1498
+ from: `Synth_Node_${fromIdx}`,
1499
+ to: `Synth_Node_${toIdx}`,
1500
+ relationType: "causal_impact_on"
1501
+ });
1502
+ }
1503
+ return {
1504
+ version: "2.1.0",
1505
+ updatedAt: new Date().toISOString(),
1506
+ totalRules: rules.length,
1507
+ totalRelations: relations.length,
1508
+ rules,
1509
+ relations
1510
+ };
1511
+ }
1512
+ async function executeOverloadBenchmark(customConfig, silent = false) {
1513
+ const config = {
1514
+ ruleScale: customConfig?.ruleScale ?? 1e4,
1515
+ relationScale: customConfig?.relationScale ?? 20000,
1516
+ concurrency: customConfig?.concurrency ?? 50,
1517
+ totalRequests: customConfig?.totalRequests ?? 5000,
1518
+ fileInputScale: customConfig?.fileInputScale ?? 1e4,
1519
+ crudIterations: customConfig?.crudIterations ?? 5000
1520
+ };
1521
+ const log = (msg) => {
1522
+ if (!silent)
1523
+ process.stdout.write(msg);
1524
+ };
1525
+ const benchStart = performance.now();
1526
+ const scenarios = [];
1527
+ const isExtreme = config.ruleScale >= 1e5;
1528
+ 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";
1529
+ const sloP50Ms = isExtreme ? 2 : 0.8;
1530
+ const sloP99Ms = isExtreme ? 5 : 1.5;
1531
+ const sloRps = isExtreme ? 3000 : 5000;
1532
+ const sloHopMs = isExtreme ? 100 : 30;
1533
+ const sloHeapDeltaMB = isExtreme ? 8 : 3;
1534
+ log(`
1535
+ ${BOLD}${CYAN}\uD83D\uDD25 [Causal Memory 2.1] ${modeTitle} \uAC00\uB3D9${RESET}
1536
+ `);
1537
+ 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}
1538
+ `);
1539
+ log(`${GRAY}${"\u2501".repeat(84)}${RESET}
1540
+
1541
+ `);
1542
+ 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}
1543
+ `);
1544
+ const genStart = performance.now();
1545
+ const synthDb = generateSyntheticDatabase(config.ruleScale, config.relationScale);
1546
+ const genDurationMs = Math.round((performance.now() - genStart) * 100) / 100;
1547
+ const indexStart = performance.now();
1548
+ const scaleStore = new CausalMemoryStore(undefined, synthDb);
1549
+ const indexBuildMs = Math.round((performance.now() - indexStart) * 100) / 100;
1550
+ const queryLatencies = [];
1551
+ let lastResonance = scaleStore.queryResonance({ keywords: ["kw_10"] });
1552
+ for (let i = 0;i < 1000; i++) {
1553
+ const targetKw = `kw_${10 + i % 5000}`;
1554
+ const qStart = performance.now();
1555
+ lastResonance = scaleStore.queryResonance({ keywords: [targetKw] });
1556
+ queryLatencies.push(performance.now() - qStart);
1557
+ }
1558
+ queryLatencies.sort((a, b) => a - b);
1559
+ const s1P50 = calcPercentile(queryLatencies, 50);
1560
+ const s1P95 = calcPercentile(queryLatencies, 95);
1561
+ const s1P99 = calcPercentile(queryLatencies, 99);
1562
+ const s1Passed = s1P50 <= sloP50Ms && s1P99 <= sloP99Ms && parseFloat(lastResonance.tokenDietSavingsRatio) >= 99;
1563
+ scenarios.push({
1564
+ name: "\uC2DC\uB098\uB9AC\uC624 1: \uB300\uADDC\uBAA8 \uBCFC\uB968 \uC801\uC7AC \uBC0F O(1) \uACF5\uBA85 \uCFFC\uB9AC",
1565
+ passed: s1Passed,
1566
+ details: {
1567
+ totalRules: config.ruleScale,
1568
+ totalRelations: config.relationScale,
1569
+ syntheticGenMs: genDurationMs,
1570
+ indexBuildMs,
1571
+ p50Ms: s1P50,
1572
+ p95Ms: s1P95,
1573
+ p99Ms: s1P99,
1574
+ tokenSavings: lastResonance.tokenDietSavingsRatio
1575
+ }
1576
+ });
1577
+ log(` \u2022 \uC5ED\uC778\uB371\uC2A4 \uAD6C\uCD95 \uC2DC\uAC04 : ${BOLD}${GREEN}${indexBuildMs}ms${RESET} (${config.ruleScale.toLocaleString()} Rules + ${config.relationScale.toLocaleString()} Relations)
1578
+ `);
1579
+ 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)
1580
+ `);
1581
+ log(` \u2022 \uD1A0\uD070 \uC808\uAC10\uB960 : ${BOLD}${GREEN}${lastResonance.tokenDietSavingsRatio}${RESET} (${lastResonance.matchedCount}/${config.ruleScale}\uAC1C\uB9CC \uC815\uBC00 \uACF5\uBA85)
1582
+ `);
1583
+ log(` \u2022 \uD310\uC815 : ${s1Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
1584
+
1585
+ `);
1586
+ 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}
1587
+ `);
1588
+ const benchPort = 4288;
1589
+ const server = startCausalServer({ port: benchPort });
1590
+ let successCount = 0;
1591
+ let errorCount = 0;
1592
+ const httpLatencies = [];
1593
+ const requestsPerWorker = Math.floor(config.totalRequests / config.concurrency);
1594
+ for (let w = 0;w < 10; w++) {
1595
+ await fetch(`http://127.0.0.1:${benchPort}/query`, {
1596
+ method: "POST",
1597
+ headers: { "Content-Type": "application/json" },
1598
+ body: JSON.stringify({ keywords: ["poe2"], format: "compact" })
1599
+ });
1600
+ }
1601
+ const concStart = performance.now();
1602
+ try {
1603
+ const workerPromises = Array.from({ length: config.concurrency }, async (_, workerId) => {
1604
+ const sampleQueries = [
1605
+ { keywords: ["poe2", "updater"], format: "compact" },
1606
+ { files: ["crates/vesper-audio-core/src/lib.rs"], format: "compact" },
1607
+ { keywords: ["synapse", "causal"], format: "compact" },
1608
+ { keywords: ["pcb", "kicad"], format: "compact" }
1609
+ ];
1610
+ for (let r = 0;r < requestsPerWorker; r++) {
1611
+ const qPayload = sampleQueries[(workerId + r) % sampleQueries.length];
1612
+ const reqT0 = performance.now();
1613
+ try {
1614
+ const resp = await fetch(`http://127.0.0.1:${benchPort}/query`, {
1615
+ method: "POST",
1616
+ headers: { "Content-Type": "application/json" },
1617
+ body: JSON.stringify(qPayload)
1618
+ });
1619
+ if (resp.ok) {
1620
+ await resp.text();
1621
+ successCount++;
1622
+ } else {
1623
+ errorCount++;
1624
+ }
1625
+ } catch {
1626
+ errorCount++;
1627
+ }
1628
+ httpLatencies.push(performance.now() - reqT0);
1629
+ }
1630
+ });
1631
+ await Promise.all(workerPromises);
1632
+ } finally {
1633
+ server.stop();
1634
+ }
1635
+ const concTotalSec = (performance.now() - concStart) / 1000;
1636
+ const rps = Math.round(successCount / concTotalSec);
1637
+ httpLatencies.sort((a, b) => a - b);
1638
+ const s2P50 = calcPercentile(httpLatencies, 50);
1639
+ const s2P95 = calcPercentile(httpLatencies, 95);
1640
+ const s2P99 = calcPercentile(httpLatencies, 99);
1641
+ const s2Passed = errorCount === 0 && successCount === config.totalRequests && rps >= sloRps;
1642
+ scenarios.push({
1643
+ name: "\uC2DC\uB098\uB9AC\uC624 2: \uACE0\uB3D9\uC2DC\uC131 \uB370\uBAAC \uCC98\uB9AC\uB7C9",
1644
+ passed: s2Passed,
1645
+ details: {
1646
+ concurrency: config.concurrency,
1647
+ totalRequests: config.totalRequests,
1648
+ successCount,
1649
+ errorCount,
1650
+ durationSec: Math.round(concTotalSec * 100) / 100,
1651
+ rps,
1652
+ httpP50Ms: s2P50,
1653
+ httpP95Ms: s2P95,
1654
+ httpP99Ms: s2P99
1655
+ }
1656
+ });
1657
+ 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})
1658
+ `);
1659
+ 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)
1660
+ `);
1661
+ log(` \u2022 HTTP \uC655\uBCF5 \uB808\uC774\uD134\uC2DC : ${BOLD}p50=${s2P50}ms | p95=${s2P95}ms | p99=${s2P99}ms${RESET}
1662
+ `);
1663
+ log(` \u2022 \uD310\uC815 : ${s2Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
1664
+
1665
+ `);
1666
+ 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}
1667
+ `);
1668
+ const massiveFiles = [];
1669
+ for (let f = 0;f < config.fileInputScale; f++) {
1670
+ massiveFiles.push(`services/microservice_${f % 100}/src/handler_${f}.ts`);
1671
+ }
1672
+ massiveFiles[Math.floor(config.fileInputScale / 2)] = "packages/mod_10/src/file_10.rs";
1673
+ const hopStart = performance.now();
1674
+ const multiHopRes = scaleStore.queryResonance({
1675
+ files: massiveFiles,
1676
+ propagateRelations: true,
1677
+ maxHops: 5
1678
+ });
1679
+ const hopDurationMs = Math.round((performance.now() - hopStart) * 100) / 100;
1680
+ const matchedTitles = new Set(multiHopRes.resonanceRules.map((r) => r.title));
1681
+ 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");
1682
+ const s3Passed = all5HopsPropagated && hopDurationMs <= sloHopMs;
1683
+ scenarios.push({
1684
+ name: "\uC2DC\uB098\uB9AC\uC624 3: \uB300\uADDC\uBAA8 \uD30C\uC77C & 5-Hop \uB2E4\uC911 \uC778\uACFC \uC804\uD30C",
1685
+ passed: s3Passed,
1686
+ details: {
1687
+ inputFilesCount: config.fileInputScale,
1688
+ maxHops: 5,
1689
+ traversalDurationMs: hopDurationMs,
1690
+ matchedRulesCount: multiHopRes.matchedCount,
1691
+ activeRelationsCount: multiHopRes.activeRelations.length,
1692
+ all5HopsResolved: all5HopsPropagated ? "YES (Circular Loop Safe)" : "NO",
1693
+ tokenSavings: multiHopRes.tokenDietSavingsRatio
1694
+ }
1695
+ });
1696
+ log(` \u2022 \uB300\uB7C9 \uD30C\uC77C + 5-Hop \uC18C\uC694 : ${BOLD}${GREEN}${hopDurationMs}ms${RESET} (\uBAA9\uD45C: \u2264${sloHopMs}ms)
1697
+ `);
1698
+ 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}
1699
+ `);
1700
+ log(` \u2022 \uC815\uBC00 \uACA9\uB9AC \uC808\uAC10\uB960 : ${BOLD}${GREEN}${multiHopRes.tokenDietSavingsRatio}${RESET} (${multiHopRes.matchedCount}/${config.ruleScale}\uAC1C\uB9CC \uD65C\uC131\uD654)
1701
+ `);
1702
+ log(` \u2022 \uD310\uC815 : ${s3Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
1703
+
1704
+ `);
1705
+ 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}
1706
+ `);
1707
+ if (typeof Bun !== "undefined" && typeof Bun.gc === "function") {
1708
+ Bun.gc(true);
1709
+ }
1710
+ const baseMem = process.memoryUsage();
1711
+ const baseHeapMB = baseMem.heapUsed / (1024 * 1024);
1712
+ const crudStore = new CausalMemoryStore(undefined, {
1713
+ version: "2.1.0",
1714
+ updatedAt: new Date().toISOString(),
1715
+ totalRules: 0,
1716
+ totalRelations: 0,
1717
+ rules: [],
1718
+ relations: []
1719
+ });
1720
+ const crudStart = performance.now();
1721
+ for (let c = 0;c < config.crudIterations; c++) {
1722
+ const tempName = `Ephemeral_Entity_${c % 50}`;
1723
+ crudStore.createEntities([
1724
+ {
1725
+ name: tempName,
1726
+ entityType: "StressTest",
1727
+ observations: [`\uC808\uB300 \uADDC\uCE59 #${c}`, `1. \uC2E4\uD589 \uB2E8\uACC4 #${c}`]
1728
+ }
1729
+ ], false);
1730
+ crudStore.addObservations([{ entityName: tempName, contents: [`\uCD94\uAC00 \uAD00\uCC30 #${c}`] }], false);
1731
+ crudStore.queryResonance({ keywords: [tempName] });
1732
+ crudStore.deleteEntities([tempName], false);
1733
+ }
1734
+ const crudDurationMs = Math.round((performance.now() - crudStart) * 100) / 100;
1735
+ if (typeof Bun !== "undefined" && typeof Bun.gc === "function") {
1736
+ Bun.gc(true);
1737
+ }
1738
+ const afterMem = process.memoryUsage();
1739
+ const afterHeapMB = afterMem.heapUsed / (1024 * 1024);
1740
+ const rssMB = Math.round(afterMem.rss / (1024 * 1024) * 100) / 100;
1741
+ const heapDeltaMB = Math.round(Math.max(0, afterHeapMB - baseHeapMB) * 100) / 100;
1742
+ const s4Passed = heapDeltaMB <= sloHeapDeltaMB && crudStore.getAllRules().length === 0;
1743
+ scenarios.push({
1744
+ name: "\uC2DC\uB098\uB9AC\uC624 4: \uC5F0\uC18D CRUD \uB0B4\uAD6C\uB3C4 \uBC0F \uD799 \uB204\uC218 \uAC10\uC0AC",
1745
+ passed: s4Passed,
1746
+ details: {
1747
+ crudCycles: config.crudIterations,
1748
+ totalOperations: config.crudIterations * 4,
1749
+ durationMs: crudDurationMs,
1750
+ baseHeapMB: Math.round(baseHeapMB * 100) / 100,
1751
+ afterHeapMB: Math.round(afterHeapMB * 100) / 100,
1752
+ heapDeltaMB,
1753
+ rssMB,
1754
+ residualRules: crudStore.getAllRules().length
1755
+ }
1756
+ });
1757
+ log(` \u2022 \uCD1D \uC218\uD589 \uC5F0\uC0B0 \uC218 : ${BOLD}${(config.crudIterations * 4).toLocaleString()}\uD68C CRUD \uC5F0\uC0B0${RESET} (\uC18C\uC694: ${crudDurationMs}ms)
1758
+ `);
1759
+ 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)
1760
+ `);
1761
+ log(` \u2022 \uD504\uB85C\uC138\uC2A4 RSS \uC810\uC720 : ${BOLD}${rssMB} MB${RESET}
1762
+ `);
1763
+ log(` \u2022 \uD310\uC815 : ${s4Passed ? `${BOLD}${GREEN}\u2705 PASS${RESET}` : `${BOLD}${RED}\u274C FAIL${RESET}`}
1764
+
1765
+ `);
1766
+ const totalDurationMs = Math.round((performance.now() - benchStart) * 100) / 100;
1767
+ const allPassed = scenarios.every((s) => s.passed);
1768
+ log(`${GRAY}${"\u2501".repeat(84)}${RESET}
1769
+ `);
1770
+ 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}
1771
+
1772
+ `);
1773
+ return {
1774
+ timestamp: new Date().toISOString(),
1775
+ config,
1776
+ allPassed,
1777
+ totalDurationMs,
1778
+ scenarios
1779
+ };
1780
+ }
1781
+ var RESET = "\x1B[0m", BOLD = "\x1B[1m", CYAN = "\x1B[36m", GREEN = "\x1B[32m", YELLOW = "\x1B[33m", RED = "\x1B[31m", GRAY = "\x1B[90m";
1782
+ var init_causal_benchmark = __esm(() => {
1783
+ init_causal_store();
1784
+ init_causal_server();
1785
+ });
1786
+
1787
+ // cli.ts
1788
+ init_causal_config();
1789
+ init_causal_store();
1790
+ init_causal_query();
1791
+ init_causal_server();
1792
+
1793
+ // causal-migrator.ts
1794
+ init_causal_config();
1795
+ init_causal_store();
1796
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
1797
+ import { dirname as dirname2 } from "path";
1798
+ function migrateLegacyMemoryGraph(sourceBackupPath = CAUSAL_MEMORY_CONFIG.LEGACY_BACKUP_PATH, targetGlobalPath = CAUSAL_MEMORY_CONFIG.GLOBAL_DB_PATH, targetLocalPath = CAUSAL_MEMORY_CONFIG.LOCAL_DB_PATH) {
1799
+ if (!existsSync2(sourceBackupPath)) {
1800
+ throw new Error(`\uAD6C\uD615 \uBA54\uBAA8\uB9AC \uBC31\uC5C5 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${sourceBackupPath}`);
1801
+ }
1802
+ const raw = readFileSync2(sourceBackupPath, "utf-8");
1803
+ const legacy = JSON.parse(raw);
1804
+ const entities = legacy.entities || [];
1805
+ const legacyRelations = legacy.relations || [];
1806
+ const today = new Date().toISOString().split("T")[0];
1807
+ const seenIds = new Map;
1808
+ const rules = [];
1809
+ for (const ent of entities) {
1810
+ const inferred = CausalMemoryStore.inferRuleFromEntity(ent, today);
1811
+ const baseId = inferred.id;
1812
+ const count = seenIds.get(baseId) || 0;
1813
+ seenIds.set(baseId, count + 1);
1814
+ const uniqueId = count === 0 ? baseId : `${baseId}-${count + 1}`;
1815
+ rules.push({
1816
+ ...inferred,
1817
+ id: uniqueId
1818
+ });
1819
+ }
1820
+ const hasVesperCore = rules.some((r) => r.id === "rule-project_vesper_dsp-vesper-dsp-master-invariants");
1821
+ if (!hasVesperCore) {
1822
+ rules.push({
1823
+ id: "rule-project_vesper_dsp-vesper-dsp-master-invariants",
1824
+ domain: "Project_Vesper_DSP",
1825
+ title: "Vesper_DSP_Master_Invariants",
1826
+ entityType: "Architecture_Invariant",
1827
+ triggers: {
1828
+ files: ["D:/Vesper", "crates/vesper-", "vesper-apo", "dsp-settings.json"],
1829
+ keywords: ["vesper", "dsp", "mmcss", "virtuallock", "\uC624\uB514\uC624", "\uC0AC\uC6B4\uB4DC", "\uB04A\uAE40", "apo"],
1830
+ commands: ["check", "build", "package", "sign"]
1831
+ },
1832
+ invariants: [
1833
+ "\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",
1834
+ "\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",
1835
+ "\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",
1836
+ "Windows \uC167\uB2E4\uC6B4 \uC2DC WM_QUERYENDSESSION / SM_SHUTTINGDOWN \uAC10\uC9C0 \uD6C4 \uC815\uC0C1 \uC885\uB8CC\uB97C \uBCF4\uC7A5\uD560 \uAC83"
1837
+ ],
1838
+ actions: [
1839
+ "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",
1840
+ "2. \uC218\uC815\uB41C \uD06C\uB808\uC774\uD2B8 \uD2B8\uB799\uB9CC \uC778\uACFC\uC728 \uC99D\uBD84 \uBE4C\uB4DC \uC218\uD589"
1841
+ ],
1842
+ context: [
1843
+ "\uC2E4\uC99D \uD0C0\uAE43 \uACBD\uB85C: D:\\Vesper\\dsp-boot-fix\\dsp",
1844
+ "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"
1845
+ ],
1846
+ lastVerified: today
1847
+ });
1848
+ }
1849
+ const hasSynapseCore = rules.some((r) => r.id === "rule-project_synapse_kit-synapse-kit-core-invariants");
1850
+ if (!hasSynapseCore) {
1851
+ rules.push({
1852
+ id: "rule-project_synapse_kit-synapse-kit-core-invariants",
1853
+ domain: "Project_Synapse_Kit",
1854
+ title: "Synapse_Kit_Core_Invariants",
1855
+ entityType: "Framework_Invariant",
1856
+ triggers: {
1857
+ files: ["D:/synapse", "D:/synapse-site", "synapse.config.ts"],
1858
+ keywords: ["synapse", "synapse-kit", "\uC2DC\uB0C5\uC2A4", "\uC778\uACFC\uC728", "causal", "\uCEF4\uD30C\uC77C\uB7EC", "\uCCB4\uCEE4"],
1859
+ commands: ["check", "build", "package", "sign", "init", "memory"]
1860
+ },
1861
+ invariants: [
1862
+ "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",
1863
+ "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",
1864
+ "\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"
1865
+ ],
1866
+ actions: [
1867
+ "1. \uCF54\uB4DC \uBCC0\uACBD \uD6C4 bun D:/synapse/dist/cli.js check --compact\uB85C \uBB34\uACB0\uC131 \uAC80\uC99D",
1868
+ "2. \uBA54\uBAA8\uB9AC \uC870\uD68C \uC2DC synapse memory query\uB85C \uACF5\uBA85 \uADDC\uCE59\uB9CC \uCD5C\uC18C \uD1A0\uD070 \uC8FC\uC785"
1869
+ ],
1870
+ context: [
1871
+ "\uC5D4\uC9C4 \uCF54\uC5B4 \uACBD\uB85C: D:\\synapse",
1872
+ "\uACF5\uC2DD \uBB38\uC11C \uBC0F \uC704\uD0A4 \uACBD\uB85C: D:\\synapse-site (KO/EN \uB2E4\uAD6D\uC5B4 \uB85C\uCF00\uC77C \uC790\uB3D9 \uBD84\uAE30)"
1873
+ ],
1874
+ lastVerified: today
1875
+ });
1876
+ }
1877
+ const relations = [...legacyRelations];
1878
+ const coreRelations = [
1879
+ {
1880
+ from: "POE2_AutoFilter_Project",
1881
+ to: "POE2_AutoFilter_ReleaseProcess",
1882
+ relationType: "governed_by_workflow"
1883
+ },
1884
+ {
1885
+ from: "POE2_AutoFilter_ReleaseProcess",
1886
+ to: "POE2_AutoFilter_Updater",
1887
+ relationType: "triggers_update_of"
1888
+ },
1889
+ {
1890
+ from: "POE2_AutoFilter_ReleaseProcess",
1891
+ to: "POE2_AutoFilter_KnownBugs",
1892
+ relationType: "verifies_and_notifies"
1893
+ },
1894
+ {
1895
+ from: "Synapse_Kit_Core_Invariants",
1896
+ to: "Vesper_DSP_Master_Invariants",
1897
+ relationType: "verifies_and_builds"
1898
+ }
1899
+ ];
1900
+ for (const cr of coreRelations) {
1901
+ const exists = relations.some((r) => r.from === cr.from && r.to === cr.to && r.relationType === cr.relationType);
1902
+ if (!exists) {
1903
+ relations.push(cr);
1904
+ }
1905
+ }
1906
+ const db = {
1907
+ version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
1908
+ updatedAt: new Date().toISOString(),
1909
+ totalRules: rules.length,
1910
+ totalRelations: relations.length,
1911
+ rules,
1912
+ relations
1913
+ };
1914
+ const serialized = JSON.stringify(db, null, 2);
1915
+ for (const outPath of [targetGlobalPath, targetLocalPath]) {
1916
+ const dir = dirname2(outPath);
1917
+ if (!existsSync2(dir)) {
1918
+ mkdirSync2(dir, { recursive: true });
1919
+ }
1920
+ writeFileSync2(outPath, serialized, "utf-8");
1921
+ }
1922
+ const alwaysActiveCount = rules.filter((r) => r.triggers.alwaysActive).length;
1923
+ const domains = Array.from(new Set(rules.map((r) => r.domain)));
1924
+ return {
1925
+ ok: true,
1926
+ totalRules: rules.length,
1927
+ totalRelations: relations.length,
1928
+ alwaysActiveCount,
1929
+ domains
1930
+ };
1931
+ }
1932
+
1933
+ // cli.ts
1934
+ var RESET2 = "\x1B[0m";
1935
+ var BOLD2 = "\x1B[1m";
1936
+ var CYAN2 = "\x1B[36m";
1937
+ var GREEN2 = "\x1B[32m";
1938
+ var RED2 = "\x1B[31m";
1939
+ var GRAY2 = "\x1B[90m";
1940
+ var YELLOW2 = "\x1B[33m";
1941
+ function printMemoryHelp() {
1942
+ process.stdout.write(`
1943
+ ${BOLD2}${CYAN2}\uD83E\uDDE0 Synapse Kit - Causal Memory Engine (\uC778\uACFC\uC728 \uC9C0\uB2A5\uD615 \uBA54\uBAA8\uB9AC CLI)${RESET2}
1944
+
1945
+ ${BOLD2}\uC0AC\uC6A9\uBC95:${RESET2}
1946
+ synapse memory <subcommand> [options]
1947
+
1948
+ ${BOLD2}\uC11C\uBE0C \uBA85\uB839\uC5B4:${RESET2}
1949
+ query \uC791\uC5C5 \uD30C\uC77C/\uD0A4\uC6CC\uB4DC\uC5D0 \uB9E4\uCE6D\uB418\uB294 \uC778\uACFC \uADDC\uCE59 \uBC0F \uAC04\uC120(Relations) \uCD08\uACBD\uB7C9 \uACF5\uBA85 \uC870\uD68C (\uAE30\uBCF8\uAC12)
1950
+ list \uB4F1\uB85D\uB41C \uB3C4\uBA54\uC778, \uC778\uACFC \uADDC\uCE59 \uBC0F \uC778\uACFC \uAC04\uC120(Relations) \uD1B5\uACC4 \uD655\uC778
1951
+ audit \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uBB34\uACB0\uC131 \uBC0F \uD1A0\uD070 \uC808\uAC10 \uC0C1\uD0DC \uAC10\uC0AC
1952
+ assert \uC2E0\uADDC \uC778\uACFC\uC728 \uADDC\uCE59 \uB4F1\uB85D \uB610\uB294 \uAC31\uC2E0 (--json '<json>')
1953
+ migrate \uAD6C\uD615 \uC9C0\uC2DD \uADF8\uB798\uD504 \uBC31\uC5C5\uBCF8\uC744 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB85C \uBB34\uC190\uC2E4 \uBCC0\uD658
1954
+ benchmark 4\uB300 \uCC28\uC6D0 \uACFC\uBD80\uD558 \uC2A4\uD2B8\uB808\uC2A4 \uBCA4\uCE58\uB9C8\uD06C \uC2E4\uD589 (--scale 10000 --concurrency 50)
1955
+ serve \uB3C5\uB9BD \uB85C\uCEEC \uBC31\uADF8\uB77C\uC6B4\uB4DC \uC11C\uBC84/\uB370\uBAAC \uAD6C\uB3D9 (0ms \uC778\uBA54\uBAA8\uB9AC HTTP & MCP API)
1956
+ mcp-stdio MCP \uD45C\uC900 \uC785\uCD9C\uB825(stdio) JSON-RPC 2.0 \uC11C\uBC84 \uAD6C\uB3D9 (mcp_config.json \uC5F0\uB3D9\uC6A9)
1957
+
1958
+ ${BOLD2}query \uC635\uC158:${RESET2}
1959
+ --files <paths...> \uD2B8\uB9AC\uAC70 \uB300\uC0C1 \uD30C\uC77C \uACBD\uB85C (\uBBF8\uC9C0\uC815 \uC2DC Git \uBCC0\uACBD \uC790\uB3D9 \uC2A4\uCE94)
1960
+ --keywords <words...> \uD2B8\uB9AC\uAC70 \uAC80\uC0C9 \uD0A4\uC6CC\uB4DC
1961
+ --command <cmd> \uD2B8\uB9AC\uAC70 \uC5F0\uB3D9 \uBA85\uB839\uC5B4 (\uC608: release, build, sign)
1962
+ --domain <domain> \uD2B9\uC815 \uB3C4\uBA54\uC778\uC73C\uB85C \uAC80\uC0C9 \uC81C\uD55C
1963
+ --format <json|md|compact> \uCD9C\uB825 \uD3EC\uB9F7 \uC9C0\uC815
1964
+
1965
+ ${BOLD2}serve \uC635\uC158:${RESET2}
1966
+ --port <number> \uC11C\uBC84 \uD3EC\uD2B8 \uBC88\uD638 (\uAE30\uBCF8: ${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT})
1967
+ --host <string> \uBC14\uC778\uB529 \uD638\uC2A4\uD2B8 (\uAE30\uBCF8: ${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST})
1968
+
1969
+ ${BOLD2}\uACF5\uD1B5 \uD50C\uB798\uADF8:${RESET2}
1970
+ --agent, --compact AI \uC5D0\uC774\uC804\uD2B8 \uC804\uC6A9 \uCD08\uACBD\uB7C9 JSON \uBAA8\uB4DC
1971
+
1972
+ `);
1973
+ }
1974
+ async function handleMemoryCommand(args, options) {
1975
+ const subArgs = args.filter((a) => a !== "memory");
1976
+ const subCommand = subArgs.find((a) => !a.startsWith("-")) || "query";
1977
+ if (subArgs.includes("--help") || subArgs.includes("-h") || subCommand === "help") {
1978
+ printMemoryHelp();
1979
+ return 0;
1980
+ }
1981
+ if (subCommand === "mcp-stdio") {
1982
+ await startCausalMcpStdioServer();
1983
+ return 0;
1984
+ }
1985
+ if (subCommand === "benchmark") {
1986
+ let ruleScale = 1e4;
1987
+ let concurrency = 50;
1988
+ let totalRequests = 5000;
1989
+ for (let i = 0;i < subArgs.length; i++) {
1990
+ if (subArgs[i] === "--scale" && subArgs[i + 1]) {
1991
+ ruleScale = parseInt(subArgs[i + 1], 10);
1992
+ } else if (subArgs[i] === "--concurrency" && subArgs[i + 1]) {
1993
+ concurrency = parseInt(subArgs[i + 1], 10);
1994
+ } else if (subArgs[i] === "--requests" && subArgs[i + 1]) {
1995
+ totalRequests = parseInt(subArgs[i + 1], 10);
1996
+ }
1997
+ }
1998
+ await Promise.resolve().then(() => init_causal_benchmark());
1999
+ const report = await executeOverloadBenchmark({
2000
+ ruleScale,
2001
+ relationScale: ruleScale * 2,
2002
+ concurrency,
2003
+ totalRequests,
2004
+ fileInputScale: ruleScale,
2005
+ crudIterations: totalRequests
2006
+ }, Boolean(options?.agentMode));
2007
+ if (options.agentMode) {
2008
+ process.stdout.write(JSON.stringify(report, null, 2) + `
2009
+ `);
2010
+ }
2011
+ return report.allPassed ? 0 : 1;
2012
+ }
2013
+ if (subCommand === "migrate") {
2014
+ const result = migrateLegacyMemoryGraph();
2015
+ if (options.agentMode) {
2016
+ process.stdout.write(JSON.stringify(result) + `
2017
+ `);
2018
+ } else {
2019
+ process.stdout.write(`
2020
+ ${BOLD2}${GREEN2}\u2728 [Causal Memory \uB9C8\uC774\uADF8\uB808\uC774\uC158 \uC644\uB8CC]${RESET2}
2021
+ ` + ` \u2022 \uBCC0\uD658\uB41C \uC778\uACFC \uADDC\uCE59(Rules) : ${BOLD2}${result.totalRules}\uAC1C${RESET2}
2022
+ ` + ` \u2022 \uBCF4\uC874\uB41C \uC778\uACFC \uAC04\uC120(Relations) : ${BOLD2}${result.totalRelations}\uAC1C${RESET2}
2023
+ ` + ` \u2022 \uC0C1\uC2DC \uD65C\uC131 \uADDC\uCE59(AlwaysActive): ${BOLD2}${result.alwaysActiveCount}\uAC1C${RESET2}
2024
+ ` + ` \u2022 \uBD84\uB958 \uB3C4\uBA54\uC778 : ${result.domains.join(", ")}
2025
+
2026
+ `);
2027
+ }
2028
+ return 0;
2029
+ }
2030
+ if (subCommand === "assert") {
2031
+ const jsonIdx = subArgs.indexOf("--json");
2032
+ if (jsonIdx < 0 || !subArgs[jsonIdx + 1]) {
2033
+ process.stderr.write(`${RED2}\u274C --json '<\uADDC\uCE59 JSON>' \uC778\uC790\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.${RESET2}
2034
+ `);
2035
+ return 1;
2036
+ }
2037
+ const rule = JSON.parse(subArgs[jsonIdx + 1]);
2038
+ const store = new CausalMemoryStore;
2039
+ store.assertRule(rule);
2040
+ if (options.agentMode) {
2041
+ process.stdout.write(JSON.stringify({ ok: true, id: rule.id }) + `
2042
+ `);
2043
+ } else {
2044
+ process.stdout.write(`${BOLD2}${GREEN2}\u2705 \uC778\uACFC \uADDC\uCE59 \uB4F1\uB85D \uC644\uB8CC: ${rule.id}${RESET2}
2045
+ `);
2046
+ }
2047
+ return 0;
2048
+ }
2049
+ if (subCommand === "query") {
2050
+ const files = [];
2051
+ const keywords = [];
2052
+ let command;
2053
+ let domain;
2054
+ let format = options.compactMode ? "compact" : options.agentMode ? "json" : "text";
2055
+ for (let i = 0;i < subArgs.length; i++) {
2056
+ const arg = subArgs[i];
2057
+ if (arg === "--files" || arg === "-f") {
2058
+ let j = i + 1;
2059
+ while (j < subArgs.length && !subArgs[j].startsWith("-")) {
2060
+ files.push(subArgs[j]);
2061
+ j++;
2062
+ }
2063
+ i = j - 1;
2064
+ } else if (arg === "--keywords" || arg === "-k") {
2065
+ let j = i + 1;
2066
+ while (j < subArgs.length && !subArgs[j].startsWith("-")) {
2067
+ keywords.push(subArgs[j]);
2068
+ j++;
2069
+ }
2070
+ i = j - 1;
2071
+ } else if (arg === "--command" || arg === "-c") {
2072
+ command = subArgs[++i];
2073
+ } else if (arg === "--domain" || arg === "-d") {
2074
+ domain = subArgs[++i];
2075
+ } else if (arg === "--format") {
2076
+ format = subArgs[++i];
2077
+ }
2078
+ }
2079
+ const result = await runCausalQuery({
2080
+ files,
2081
+ keywords,
2082
+ command,
2083
+ domain,
2084
+ autoGit: files.length === 0
2085
+ });
2086
+ if (options.compactMode || format === "compact") {
2087
+ process.stdout.write(formatResonanceForAgent(result, true) + `
2088
+ `);
2089
+ } else if (options.agentMode || format === "json") {
2090
+ process.stdout.write(JSON.stringify(result, null, 2) + `
2091
+ `);
2092
+ } else if (format === "md" || format === "markdown") {
2093
+ process.stdout.write(formatResonanceForAgent(result, false) + `
2094
+ `);
2095
+ } else {
2096
+ process.stdout.write(`
2097
+ ${BOLD2}${CYAN2}\uD83E\uDDE0 [Causal Memory] \uACF5\uBA85 \uCFFC\uB9AC \uC644\uACB0 (${result.matchedCount}/${result.totalDatabaseRules}\uAC1C \uADDC\uCE59 \uD65C\uC131\uD654, \uAC04\uC120 ${result.activeRelations.length}\uAC1C, \uC808\uAC10\uB960: ${result.tokenDietSavingsRatio}, ${result.queryDurationMs}ms)${RESET2}
2098
+ `);
2099
+ for (const rule of result.resonanceRules) {
2100
+ process.stdout.write(`
2101
+ ${BOLD2}${GREEN2}\u2714 [${rule.domain}]${RESET2} ${BOLD2}${rule.title}${RESET2} ${GRAY2}(${rule.id})${RESET2}
2102
+ `);
2103
+ if (rule.invariants.length > 0) {
2104
+ process.stdout.write(` ${YELLOW2}\uD83D\uDEE1\uFE0F \uBD88\uBCC0 \uADDC\uCE59:${RESET2}
2105
+ `);
2106
+ for (const inv of rule.invariants) {
2107
+ process.stdout.write(` \u2022 ${inv}
2108
+ `);
2109
+ }
2110
+ }
2111
+ if (rule.actions.length > 0) {
2112
+ process.stdout.write(` ${CYAN2}\u26A1 \uC2E4\uD589 \uC9C0\uCE68:${RESET2}
2113
+ `);
2114
+ for (const act of rule.actions) {
2115
+ process.stdout.write(` \u2022 ${act}
2116
+ `);
2117
+ }
2118
+ }
2119
+ }
2120
+ if (result.activeRelations.length > 0) {
2121
+ process.stdout.write(`
2122
+ ${BOLD2}${CYAN2}\uD83D\uDD17 \uD65C\uC131 \uC778\uACFC \uAC04\uC120 (Relations):${RESET2}
2123
+ `);
2124
+ for (const rel of result.activeRelations) {
2125
+ process.stdout.write(` \u2022 ${rel.from} \u2500\u2500[${rel.relationType}]\u2500\u2500\u25B6 ${rel.to}
2126
+ `);
2127
+ }
2128
+ }
2129
+ process.stdout.write(`
2130
+ `);
2131
+ }
2132
+ return 0;
2133
+ }
2134
+ if (subCommand === "list") {
2135
+ const store = new CausalMemoryStore;
2136
+ const status = store.getStatus();
2137
+ const rules = store.getAllRules();
2138
+ if (options.agentMode) {
2139
+ process.stdout.write(JSON.stringify({ ok: true, status, rulesCount: rules.length, relationsCount: status.totalRelations }) + `
2140
+ `);
2141
+ return 0;
2142
+ }
2143
+ process.stdout.write(`
2144
+ ${BOLD2}${CYAN2}\uD83E\uDDE0 [Causal Memory] \uC778\uACFC\uC728 \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC0C1\uD0DC${RESET2}
2145
+ `);
2146
+ process.stdout.write(` \u2022 \uC800\uC7A5\uC18C \uACBD\uB85C : ${GRAY2}${status.dbPath}${RESET2}
2147
+ `);
2148
+ process.stdout.write(` \u2022 \uC5D4\uC9C4 \uBC84\uC804 : ${GREEN2}v${status.version}${RESET2}
2149
+ `);
2150
+ process.stdout.write(` \u2022 \uCD1D \uADDC\uCE59 \uC218 : ${BOLD2}${status.totalRules}\uAC1C${RESET2}
2151
+ `);
2152
+ process.stdout.write(` \u2022 \uCD1D \uC778\uACFC \uAC04\uC120: ${BOLD2}${status.totalRelations}\uAC1C${RESET2}
2153
+ `);
2154
+ process.stdout.write(` \u2022 \uAD00\uB9AC \uB3C4\uBA54\uC778 : ${status.domains.join(", ")}
2155
+
2156
+ `);
2157
+ for (const domain of status.domains) {
2158
+ const domainRules = rules.filter((r) => r.domain === domain);
2159
+ process.stdout.write(` ${BOLD2}${YELLOW2}[${domain}]${RESET2} (${domainRules.length}\uAC1C \uADDC\uCE59)
2160
+ `);
2161
+ for (const r of domainRules) {
2162
+ process.stdout.write(` \u2022 ${r.title} ${GRAY2}(${r.id})${RESET2}
2163
+ `);
2164
+ }
2165
+ }
2166
+ process.stdout.write(`
2167
+ `);
2168
+ return 0;
2169
+ }
2170
+ if (subCommand === "audit") {
2171
+ const store = new CausalMemoryStore;
2172
+ const audit = store.audit();
2173
+ if (options.agentMode) {
2174
+ process.stdout.write(JSON.stringify(audit) + `
2175
+ `);
2176
+ return audit.ok ? 0 : 1;
2177
+ }
2178
+ if (audit.ok) {
2179
+ process.stdout.write(`
2180
+ ${BOLD2}${GREEN2}\u2728 [Causal Memory Audit \uD1B5\uACFC] \uCD1D ${audit.totalRules}\uAC1C \uADDC\uCE59, ${audit.totalRelations}\uAC1C \uC778\uACFC \uAC04\uC120 (\uC0C1\uC2DC \uD65C\uC131: ${audit.alwaysActiveCount}\uAC1C) \uBB34\uACB0\uC131 100% \uC815\uC0C1${RESET2}
2181
+
2182
+ `);
2183
+ return 0;
2184
+ } else {
2185
+ process.stderr.write(`
2186
+ ${BOLD2}${RED2}\uD83D\uDEA8 [Causal Memory Audit \uACB0\uD568 \uBC1C\uACAC] \uCD1D ${audit.issues.length}\uAC1C \uD56D\uBAA9 \uC774\uC0C1 \uBC1C\uC0DD:${RESET2}
2187
+ `);
2188
+ for (const issue of audit.issues) {
2189
+ process.stderr.write(` \u2022 ${issue}
2190
+ `);
2191
+ }
2192
+ process.stderr.write(`
2193
+ `);
2194
+ return 1;
2195
+ }
2196
+ }
2197
+ if (subCommand === "serve") {
2198
+ let port = CAUSAL_MEMORY_CONFIG.DEFAULT_PORT;
2199
+ let host = CAUSAL_MEMORY_CONFIG.DEFAULT_HOST;
2200
+ for (let i = 0;i < subArgs.length; i++) {
2201
+ if (subArgs[i] === "--port" && subArgs[i + 1]) {
2202
+ port = parseInt(subArgs[i + 1], 10);
2203
+ } else if (subArgs[i] === "--host" && subArgs[i + 1]) {
2204
+ host = subArgs[i + 1];
2205
+ }
2206
+ }
2207
+ const server = startCausalServer({ port, host });
2208
+ if (options.agentMode) {
2209
+ process.stdout.write(JSON.stringify({ ok: true, port: server.port, host, url: `http://${host}:${server.port}` }) + `
2210
+ `);
2211
+ } else {
2212
+ process.stdout.write(`
2213
+ ${BOLD2}${GREEN2}\uD83D\uDE80 [Causal Memory Daemon \uAC00\uB3D9 \uC644\uB8CC]${RESET2}
2214
+ \u2022 \uC8FC\uC18C: ${CYAN2}http://${host}:${server.port}${RESET2}
2215
+ \u2022 MCP API: ${GRAY2}http://${host}:${server.port}/mcp${RESET2}
2216
+ \u2022 0ms \uC778\uBA54\uBAA8\uB9AC \uACF5\uBA85 \uB300\uAE30 \uC911... (\uC885\uB8CC: Ctrl+C)
2217
+
2218
+ `);
2219
+ }
2220
+ await new Promise(() => {});
2221
+ return 0;
2222
+ }
2223
+ process.stderr.write(`${RED2}\u274C \uC54C \uC218 \uC5C6\uB294 memory \uC11C\uBE0C \uBA85\uB839\uC5B4: '${subCommand}'${RESET2}
2224
+ `);
2225
+ printMemoryHelp();
2226
+ return 1;
2227
+ }
2228
+ export {
2229
+ handleMemoryCommand
2230
+ };