synapse-causal-memory 0.2.2 → 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/causal-memory.json +1259 -344
- package/dist/causal-config.d.ts +4 -0
- package/dist/causal-peer.d.ts +53 -0
- package/dist/causal-server.d.ts +3 -2
- package/dist/causal-store.d.ts +26 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +532 -12
- package/dist/index.js +166 -6
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@ var CAUSAL_MEMORY_CONFIG = {
|
|
|
9
9
|
LOCAL_DB_PATH: resolve(import.meta.dir, "causal-memory.json"),
|
|
10
10
|
DEFAULT_HOST: process.env.SYNAPSE_MEMORY_HOST || "127.0.0.1",
|
|
11
11
|
DEFAULT_PORT: parseInt(process.env.SYNAPSE_MEMORY_PORT || "4242", 10),
|
|
12
|
+
REMOTE_URL: process.env.SYNAPSE_MEMORY_REMOTE_URL || "",
|
|
13
|
+
REMOTE_TIMEOUT_MS: parseInt(process.env.SYNAPSE_MEMORY_REMOTE_TIMEOUT || "3000", 10),
|
|
12
14
|
MCP_PROTOCOL_VERSION: process.env.SYNAPSE_MCP_VERSION || "2024-11-05",
|
|
13
15
|
MCP_SERVER_NAME: "synapse-causal-memory",
|
|
14
16
|
MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10)
|
|
@@ -164,6 +166,42 @@ class CausalMemoryStore {
|
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
}
|
|
169
|
+
static getRuleDomainPriority(rule) {
|
|
170
|
+
const titleLower = rule.title.toLowerCase();
|
|
171
|
+
const dom = rule.domain;
|
|
172
|
+
if (titleLower.includes("biophys") && dom === "User_Preferences_And_Rules") {
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
if (dom === "Knowledge_Base") {
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
return 2;
|
|
179
|
+
}
|
|
180
|
+
static deduplicateRulesByTitle(rules) {
|
|
181
|
+
const byTitle = new Map;
|
|
182
|
+
for (const rule of rules) {
|
|
183
|
+
if (!rule.title)
|
|
184
|
+
continue;
|
|
185
|
+
const key = rule.title.toLowerCase();
|
|
186
|
+
const existing = byTitle.get(key);
|
|
187
|
+
if (!existing) {
|
|
188
|
+
byTitle.set(key, rule);
|
|
189
|
+
} else {
|
|
190
|
+
const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
|
|
191
|
+
const incomingPrio = CausalMemoryStore.getRuleDomainPriority(rule);
|
|
192
|
+
if (incomingPrio > existingPrio) {
|
|
193
|
+
byTitle.set(key, rule);
|
|
194
|
+
} else if (incomingPrio === existingPrio) {
|
|
195
|
+
const existingDate = existing.lastVerified || "";
|
|
196
|
+
const incomingDate = rule.lastVerified || "";
|
|
197
|
+
if (incomingDate >= existingDate) {
|
|
198
|
+
byTitle.set(key, rule);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return Array.from(byTitle.values());
|
|
204
|
+
}
|
|
167
205
|
loadDatabase() {
|
|
168
206
|
try {
|
|
169
207
|
if (existsSync(this.dbPath)) {
|
|
@@ -171,7 +209,8 @@ class CausalMemoryStore {
|
|
|
171
209
|
this.lastMtimeMs = stat.mtimeMs;
|
|
172
210
|
const raw = readFileSync(this.dbPath, "utf-8");
|
|
173
211
|
const parsed = JSON.parse(raw);
|
|
174
|
-
const
|
|
212
|
+
const rawRules = Array.isArray(parsed.rules) ? parsed.rules : [];
|
|
213
|
+
const rules = CausalMemoryStore.deduplicateRulesByTitle(rawRules);
|
|
175
214
|
const relations = Array.isArray(parsed.relations) ? parsed.relations : [];
|
|
176
215
|
return {
|
|
177
216
|
version: parsed.version || CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
@@ -620,6 +659,34 @@ class CausalMemoryStore {
|
|
|
620
659
|
}
|
|
621
660
|
return created;
|
|
622
661
|
}
|
|
662
|
+
deleteRule(ruleId, persistToDisk = true) {
|
|
663
|
+
const targetId = ruleId.toLowerCase();
|
|
664
|
+
const removedTitles = new Set;
|
|
665
|
+
const filteredRules = this.database.rules.filter((r) => {
|
|
666
|
+
if (r.id.toLowerCase() === targetId) {
|
|
667
|
+
removedTitles.add(r.title.toLowerCase());
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
return true;
|
|
671
|
+
});
|
|
672
|
+
if (removedTitles.size === 0) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
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);
|
|
676
|
+
this.database = {
|
|
677
|
+
...this.database,
|
|
678
|
+
updatedAt: new Date().toISOString(),
|
|
679
|
+
totalRules: filteredRules.length,
|
|
680
|
+
totalRelations: filteredRelations.length,
|
|
681
|
+
rules: filteredRules,
|
|
682
|
+
relations: filteredRelations
|
|
683
|
+
};
|
|
684
|
+
this.rebuildIndices();
|
|
685
|
+
if (persistToDisk) {
|
|
686
|
+
this.saveDatabase();
|
|
687
|
+
}
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
623
690
|
deleteEntities(entityNames, persistToDisk = true) {
|
|
624
691
|
const nameSet = new Set(entityNames.map((n) => n.toLowerCase()));
|
|
625
692
|
const filteredRules = this.database.rules.filter((r) => !nameSet.has(r.title.toLowerCase()) && !nameSet.has(r.id.toLowerCase()));
|
|
@@ -684,7 +751,7 @@ class CausalMemoryStore {
|
|
|
684
751
|
const invariants = [];
|
|
685
752
|
const actions = [];
|
|
686
753
|
const context = [];
|
|
687
|
-
const isTrueUserPreference = typeLower === "preference" || typeLower === "user_constraint" || typeLower === "forbidden_rules" || nameLower === "userpreferences" || nameLower === "user_web_navigation_preference" || nameLower === "python_strictly_forbidden" || nameLower === "forbidden_topics_until_mentioned";
|
|
754
|
+
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");
|
|
688
755
|
if (isTrueUserPreference) {
|
|
689
756
|
domain = "User_Preferences_And_Rules";
|
|
690
757
|
triggers.alwaysActive = true;
|
|
@@ -746,6 +813,7 @@ class CausalMemoryStore {
|
|
|
746
813
|
audit() {
|
|
747
814
|
const issues = [];
|
|
748
815
|
const seenIds = new Set;
|
|
816
|
+
const seenTitles = new Set;
|
|
749
817
|
let alwaysActiveCount = 0;
|
|
750
818
|
for (const rule of this.database.rules) {
|
|
751
819
|
if (rule.triggers.alwaysActive) {
|
|
@@ -758,6 +826,14 @@ class CausalMemoryStore {
|
|
|
758
826
|
} else {
|
|
759
827
|
seenIds.add(rule.id);
|
|
760
828
|
}
|
|
829
|
+
const titleLower = (rule.title || "").toLowerCase();
|
|
830
|
+
if (titleLower) {
|
|
831
|
+
if (seenTitles.has(titleLower)) {
|
|
832
|
+
issues.push(`\uC911\uBCF5 \uC81C\uBAA9(Title) \uBC1C\uACAC: ${rule.title}`);
|
|
833
|
+
} else {
|
|
834
|
+
seenTitles.add(titleLower);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
761
837
|
if (!rule.domain) {
|
|
762
838
|
issues.push(`\uB3C4\uBA54\uC778 \uB204\uB77D \uADDC\uCE59: ${rule.id}`);
|
|
763
839
|
}
|
|
@@ -773,6 +849,76 @@ class CausalMemoryStore {
|
|
|
773
849
|
issues
|
|
774
850
|
};
|
|
775
851
|
}
|
|
852
|
+
getRawDatabase() {
|
|
853
|
+
this.reloadIfNeeded();
|
|
854
|
+
return this.database;
|
|
855
|
+
}
|
|
856
|
+
mergeDatabase(incomingRules, incomingRelations = []) {
|
|
857
|
+
this.reloadIfNeeded();
|
|
858
|
+
let addedRules = 0;
|
|
859
|
+
let updatedRules = 0;
|
|
860
|
+
let addedRelations = 0;
|
|
861
|
+
const byTitle = new Map;
|
|
862
|
+
for (const r of this.database.rules) {
|
|
863
|
+
byTitle.set(r.title.toLowerCase(), r);
|
|
864
|
+
}
|
|
865
|
+
for (const incoming of incomingRules) {
|
|
866
|
+
if (!incoming.id || !incoming.title)
|
|
867
|
+
continue;
|
|
868
|
+
const titleKey = incoming.title.toLowerCase();
|
|
869
|
+
const existing = byTitle.get(titleKey);
|
|
870
|
+
if (!existing) {
|
|
871
|
+
byTitle.set(titleKey, incoming);
|
|
872
|
+
addedRules++;
|
|
873
|
+
} else {
|
|
874
|
+
const existingPrio = CausalMemoryStore.getRuleDomainPriority(existing);
|
|
875
|
+
const incomingPrio = CausalMemoryStore.getRuleDomainPriority(incoming);
|
|
876
|
+
const existingDate = existing.lastVerified || "";
|
|
877
|
+
const incomingDate = incoming.lastVerified || "";
|
|
878
|
+
if (incomingPrio > existingPrio) {
|
|
879
|
+
byTitle.set(titleKey, incoming);
|
|
880
|
+
updatedRules++;
|
|
881
|
+
} else if (incomingPrio === existingPrio && incoming.id !== existing.id && incomingDate > existingDate) {
|
|
882
|
+
byTitle.set(titleKey, incoming);
|
|
883
|
+
updatedRules++;
|
|
884
|
+
} else if (incomingPrio === existingPrio && incoming.id === existing.id && incomingDate > existingDate) {
|
|
885
|
+
byTitle.set(titleKey, incoming);
|
|
886
|
+
updatedRules++;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const relationKey = (rel) => `${rel.from.toLowerCase()}::${rel.relationType.toLowerCase()}::${rel.to.toLowerCase()}`;
|
|
891
|
+
const relMap = new Map;
|
|
892
|
+
for (const rel of this.database.relations) {
|
|
893
|
+
relMap.set(relationKey(rel), rel);
|
|
894
|
+
}
|
|
895
|
+
for (const incomingRel of incomingRelations) {
|
|
896
|
+
const key = relationKey(incomingRel);
|
|
897
|
+
if (!relMap.has(key)) {
|
|
898
|
+
relMap.set(key, incomingRel);
|
|
899
|
+
addedRelations++;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const mergedRules = Array.from(byTitle.values());
|
|
903
|
+
const mergedRelations = Array.from(relMap.values());
|
|
904
|
+
this.database = {
|
|
905
|
+
...this.database,
|
|
906
|
+
updatedAt: new Date().toISOString(),
|
|
907
|
+
totalRules: mergedRules.length,
|
|
908
|
+
totalRelations: mergedRelations.length,
|
|
909
|
+
rules: mergedRules,
|
|
910
|
+
relations: mergedRelations
|
|
911
|
+
};
|
|
912
|
+
this.rebuildIndices();
|
|
913
|
+
this.saveDatabase();
|
|
914
|
+
return {
|
|
915
|
+
addedRules,
|
|
916
|
+
updatedRules,
|
|
917
|
+
addedRelations,
|
|
918
|
+
totalRules: mergedRules.length,
|
|
919
|
+
totalRelations: mergedRelations.length
|
|
920
|
+
};
|
|
921
|
+
}
|
|
776
922
|
}
|
|
777
923
|
// causal-query.ts
|
|
778
924
|
var {spawn } = globalThis.Bun;
|
|
@@ -1320,6 +1466,17 @@ function startCausalServer(options = {}) {
|
|
|
1320
1466
|
const auditResult = store.audit();
|
|
1321
1467
|
return new Response(JSON.stringify(auditResult), { headers });
|
|
1322
1468
|
}
|
|
1469
|
+
if (url.pathname === "/sync" && req.method === "GET") {
|
|
1470
|
+
const rawDb = store.getRawDatabase();
|
|
1471
|
+
return new Response(JSON.stringify(rawDb), { headers });
|
|
1472
|
+
}
|
|
1473
|
+
if (url.pathname === "/sync" && req.method === "POST") {
|
|
1474
|
+
const body = await req.json();
|
|
1475
|
+
const incomingRules = Array.isArray(body.rules) ? body.rules : [];
|
|
1476
|
+
const incomingRelations = Array.isArray(body.relations) ? body.relations : [];
|
|
1477
|
+
const mergeResult = store.mergeDatabase(incomingRules, incomingRelations);
|
|
1478
|
+
return new Response(JSON.stringify({ ok: true, ...mergeResult }), { headers });
|
|
1479
|
+
}
|
|
1323
1480
|
if (url.pathname === "/mcp" && req.method === "POST") {
|
|
1324
1481
|
const rpc = await req.json();
|
|
1325
1482
|
const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath);
|
|
@@ -1336,9 +1493,12 @@ function startCausalServer(options = {}) {
|
|
|
1336
1493
|
});
|
|
1337
1494
|
return server;
|
|
1338
1495
|
}
|
|
1339
|
-
async function startCausalMcpStdioServer(dbPath) {
|
|
1496
|
+
async function startCausalMcpStdioServer(dbPath, remoteUrl) {
|
|
1340
1497
|
const store = new CausalMemoryStore(dbPath);
|
|
1341
|
-
const
|
|
1498
|
+
const targetRemote = remoteUrl || CAUSAL_MEMORY_CONFIG.REMOTE_URL;
|
|
1499
|
+
const isRemoteMode = Boolean(targetRemote && targetRemote.trim().length > 0);
|
|
1500
|
+
const activeDaemonUrl = isRemoteMode ? targetRemote.endsWith("/mcp") ? targetRemote : `${targetRemote.replace(/\/+$/, "")}/mcp` : `http://${CAUSAL_MEMORY_CONFIG.DEFAULT_HOST}:${CAUSAL_MEMORY_CONFIG.DEFAULT_PORT}/mcp`;
|
|
1501
|
+
const timeoutMs = isRemoteMode ? CAUSAL_MEMORY_CONFIG.REMOTE_TIMEOUT_MS : 150;
|
|
1342
1502
|
const rl = createInterface({
|
|
1343
1503
|
input: process.stdin,
|
|
1344
1504
|
terminal: false
|
|
@@ -1352,11 +1512,11 @@ async function startCausalMcpStdioServer(dbPath) {
|
|
|
1352
1512
|
let responseObj = null;
|
|
1353
1513
|
if (rpc.method === "tools/call") {
|
|
1354
1514
|
try {
|
|
1355
|
-
const daemonRes = await fetch(
|
|
1515
|
+
const daemonRes = await fetch(activeDaemonUrl, {
|
|
1356
1516
|
method: "POST",
|
|
1357
1517
|
headers: { "Content-Type": "application/json" },
|
|
1358
1518
|
body: trimmed,
|
|
1359
|
-
signal: AbortSignal.timeout(
|
|
1519
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1360
1520
|
});
|
|
1361
1521
|
if (daemonRes.ok) {
|
|
1362
1522
|
responseObj = await daemonRes.json();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synapse-causal-memory",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "AI-Native 0-Token Causal Memory Engine 2.0 with SQLite WAL atomic transactions and MCP server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,7 +21,13 @@
|
|
|
21
21
|
"build:bundle": "bun build cli.ts --outfile dist/cli.js --target bun && bun build index.ts --outfile dist/index.js --target bun",
|
|
22
22
|
"build:types": "tsc -p tsconfig.build.json",
|
|
23
23
|
"build": "bun run build:bundle && bun run build:types",
|
|
24
|
-
"prepublishOnly": "bun run build"
|
|
24
|
+
"prepublishOnly": "bun run build",
|
|
25
|
+
"start": "bun run cli.ts",
|
|
26
|
+
"query": "bun run cli.ts query",
|
|
27
|
+
"list": "bun run cli.ts list",
|
|
28
|
+
"audit": "bun run cli.ts audit",
|
|
29
|
+
"serve": "bun run cli.ts serve",
|
|
30
|
+
"bench": "bun run cli.ts benchmark"
|
|
25
31
|
},
|
|
26
32
|
"repository": {
|
|
27
33
|
"type": "git",
|