synapse-causal-memory 0.0.0-stage → 0.2.3

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