synapse-causal-memory 0.2.3 → 0.3.0
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 +35 -2
- package/dist/causal-branch.d.ts +79 -0
- package/dist/causal-config.d.ts +10 -0
- package/dist/causal-context.d.ts +113 -0
- package/dist/causal-graveyard.d.ts +94 -0
- package/dist/causal-lens.d.ts +133 -0
- package/dist/causal-predictor.d.ts +42 -0
- package/dist/causal-server.d.ts +279 -1
- package/dist/causal-store.d.ts +7 -0
- package/dist/causal-synthesizer.d.ts +96 -0
- package/dist/causal-timeseries.d.ts +92 -0
- package/dist/cli.js +2964 -46
- package/dist/index.d.ts +7 -0
- package/dist/index.js +2402 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,12 @@ var init_causal_config = __esm(() => {
|
|
|
28
28
|
REMOTE_TIMEOUT_MS: parseInt(process.env.SYNAPSE_MEMORY_REMOTE_TIMEOUT || "3000", 10),
|
|
29
29
|
MCP_PROTOCOL_VERSION: process.env.SYNAPSE_MCP_VERSION || "2024-11-05",
|
|
30
30
|
MCP_SERVER_NAME: "synapse-causal-memory",
|
|
31
|
-
MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10)
|
|
31
|
+
MAX_CONTEXT_PREVIEW_LINES: parseInt(process.env.SYNAPSE_MEMORY_MAX_PREVIEW || "5", 10),
|
|
32
|
+
CONTEXT_DB_PATH: process.env.SYNAPSE_CONTEXT_PATH || resolve(homedir(), ".gemini", "antigravity", "causal_contexts.json"),
|
|
33
|
+
LOCAL_CONTEXT_DB_PATH: resolve(import.meta.dir, "causal-contexts.json"),
|
|
34
|
+
MAX_CHECKPOINTS_RETAINED: parseInt(process.env.SYNAPSE_MAX_CHECKPOINTS || "50", 10),
|
|
35
|
+
DEFAULT_OBSIDIAN_VAULT_PATH: process.env.SYNAPSE_OBSIDIAN_VAULT || resolve(homedir(), "Documents", "Min", "Min"),
|
|
36
|
+
OBSIDIAN_LENS_SUBDIR: process.env.SYNAPSE_LENS_SUBDIR || "Synapse-Mental"
|
|
32
37
|
};
|
|
33
38
|
});
|
|
34
39
|
|
|
@@ -357,7 +362,8 @@ class CausalMemoryStore {
|
|
|
357
362
|
checkedDirs.add(twoSeg);
|
|
358
363
|
registerHits(this.exactFileIndex.get(twoSeg));
|
|
359
364
|
const oneSeg = nf.slice(0, firstSlash);
|
|
360
|
-
|
|
365
|
+
const isDriveLetter = oneSeg.length === 2 && oneSeg.endsWith(":");
|
|
366
|
+
if (!isDriveLetter && oneSeg !== twoSeg && !checkedDirs.has(oneSeg)) {
|
|
361
367
|
checkedDirs.add(oneSeg);
|
|
362
368
|
registerHits(this.exactFileIndex.get(oneSeg));
|
|
363
369
|
}
|
|
@@ -377,7 +383,7 @@ class CausalMemoryStore {
|
|
|
377
383
|
if (matchedRulesMap.has(entry.rule.id))
|
|
378
384
|
continue;
|
|
379
385
|
for (const pattern of entry.cleanPatterns) {
|
|
380
|
-
if (prefixArr.some((p) => p.includes(pattern) || pattern.includes(p))) {
|
|
386
|
+
if (prefixArr.some((p) => p.includes(pattern) || p.length >= 4 && pattern.includes(p))) {
|
|
381
387
|
matchedRulesMap.set(entry.rule.id, entry.rule);
|
|
382
388
|
nonAlwaysMatchedTitles.add(entry.rule.title.toLowerCase());
|
|
383
389
|
break;
|
|
@@ -463,7 +469,8 @@ class CausalMemoryStore {
|
|
|
463
469
|
};
|
|
464
470
|
}
|
|
465
471
|
assertRule(rule, persistToDisk = true) {
|
|
466
|
-
const
|
|
472
|
+
const targetTitle = (rule.title || rule.name || "").toLowerCase();
|
|
473
|
+
const existingIndex = this.database.rules.findIndex((r) => r.id === rule.id || targetTitle.length > 0 && (r.title || r.name || "").toLowerCase() === targetTitle);
|
|
467
474
|
const updatedRules = [...this.database.rules];
|
|
468
475
|
const today = new Date().toISOString().split("T")[0];
|
|
469
476
|
if (existingIndex >= 0) {
|
|
@@ -1038,10 +1045,1972 @@ var init_causal_query = __esm(() => {
|
|
|
1038
1045
|
init_causal_store();
|
|
1039
1046
|
});
|
|
1040
1047
|
|
|
1048
|
+
// causal-context.ts
|
|
1049
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, renameSync, statSync as statSync2 } from "fs";
|
|
1050
|
+
import { dirname as dirname2 } from "path";
|
|
1051
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
1052
|
+
async function detectGitHeadInfo(cwd = process.cwd()) {
|
|
1053
|
+
try {
|
|
1054
|
+
const branchProc = spawn2(["git", "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
1055
|
+
cwd,
|
|
1056
|
+
stdout: "pipe",
|
|
1057
|
+
stderr: "pipe"
|
|
1058
|
+
});
|
|
1059
|
+
const branchOut = (await new Response(branchProc.stdout).text()).trim();
|
|
1060
|
+
const branchExit = await branchProc.exited;
|
|
1061
|
+
const commitProc = spawn2(["git", "rev-parse", "--short", "HEAD"], {
|
|
1062
|
+
cwd,
|
|
1063
|
+
stdout: "pipe",
|
|
1064
|
+
stderr: "pipe"
|
|
1065
|
+
});
|
|
1066
|
+
const commitOut = (await new Response(commitProc.stdout).text()).trim();
|
|
1067
|
+
const commitExit = await commitProc.exited;
|
|
1068
|
+
return {
|
|
1069
|
+
branch: branchExit === 0 && branchOut.length > 0 ? branchOut : undefined,
|
|
1070
|
+
commit: commitExit === 0 && commitOut.length > 0 ? commitOut : undefined
|
|
1071
|
+
};
|
|
1072
|
+
} catch {
|
|
1073
|
+
return {};
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
class ContextCheckpointStore {
|
|
1078
|
+
dbPath;
|
|
1079
|
+
memoryDbPath;
|
|
1080
|
+
database;
|
|
1081
|
+
lastMtimeMs = 0;
|
|
1082
|
+
constructor(customPath, memoryDbPath) {
|
|
1083
|
+
this.dbPath = customPath || CAUSAL_MEMORY_CONFIG.CONTEXT_DB_PATH;
|
|
1084
|
+
this.memoryDbPath = memoryDbPath;
|
|
1085
|
+
this.database = this.loadDatabase();
|
|
1086
|
+
}
|
|
1087
|
+
loadDatabase() {
|
|
1088
|
+
try {
|
|
1089
|
+
if (existsSync2(this.dbPath)) {
|
|
1090
|
+
const stat = statSync2(this.dbPath);
|
|
1091
|
+
this.lastMtimeMs = stat.mtimeMs;
|
|
1092
|
+
const raw = readFileSync2(this.dbPath, "utf-8");
|
|
1093
|
+
const parsed = JSON.parse(raw);
|
|
1094
|
+
if (parsed && Array.isArray(parsed.checkpoints)) {
|
|
1095
|
+
return parsed;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
} catch {}
|
|
1099
|
+
return {
|
|
1100
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1101
|
+
updatedAt: new Date().toISOString(),
|
|
1102
|
+
latestCheckpointId: null,
|
|
1103
|
+
totalCheckpoints: 0,
|
|
1104
|
+
checkpoints: []
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
reloadIfNeeded() {
|
|
1108
|
+
try {
|
|
1109
|
+
if (existsSync2(this.dbPath)) {
|
|
1110
|
+
const stat = statSync2(this.dbPath);
|
|
1111
|
+
if (stat.mtimeMs > this.lastMtimeMs) {
|
|
1112
|
+
this.database = this.loadDatabase();
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
} catch {}
|
|
1116
|
+
}
|
|
1117
|
+
saveDatabase() {
|
|
1118
|
+
try {
|
|
1119
|
+
const dir = dirname2(this.dbPath);
|
|
1120
|
+
if (!existsSync2(dir)) {
|
|
1121
|
+
mkdirSync2(dir, { recursive: true });
|
|
1122
|
+
}
|
|
1123
|
+
const serialized = JSON.stringify(this.database, null, 2);
|
|
1124
|
+
const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
|
|
1125
|
+
writeFileSync2(tempPath, serialized, "utf-8");
|
|
1126
|
+
renameSync(tempPath, this.dbPath);
|
|
1127
|
+
try {
|
|
1128
|
+
const stat = statSync2(this.dbPath);
|
|
1129
|
+
this.lastMtimeMs = stat.mtimeMs;
|
|
1130
|
+
} catch {}
|
|
1131
|
+
if (this.dbPath === CAUSAL_MEMORY_CONFIG.CONTEXT_DB_PATH) {
|
|
1132
|
+
try {
|
|
1133
|
+
const localDir = dirname2(CAUSAL_MEMORY_CONFIG.LOCAL_CONTEXT_DB_PATH);
|
|
1134
|
+
if (existsSync2(localDir)) {
|
|
1135
|
+
writeFileSync2(CAUSAL_MEMORY_CONFIG.LOCAL_CONTEXT_DB_PATH, serialized, "utf-8");
|
|
1136
|
+
}
|
|
1137
|
+
} catch {}
|
|
1138
|
+
}
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
throw new Error(`\uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
static generateCheckpointId() {
|
|
1144
|
+
const now = new Date;
|
|
1145
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1146
|
+
const datePart = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
|
|
1147
|
+
const timePart = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
1148
|
+
const randPart = Math.random().toString(36).slice(2, 6);
|
|
1149
|
+
return `ctx_${datePart}_${timePart}_${randPart}`;
|
|
1150
|
+
}
|
|
1151
|
+
async saveCheckpoint(input) {
|
|
1152
|
+
this.reloadIfNeeded();
|
|
1153
|
+
if (!input.title || input.title.trim().length === 0) {
|
|
1154
|
+
throw new Error("\uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC81C\uBAA9(title)\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1155
|
+
}
|
|
1156
|
+
const cwd = input.cwd || process.cwd();
|
|
1157
|
+
const sessionId = (input.sessionId || "default-session").trim();
|
|
1158
|
+
const autoGit = input.autoGit !== false;
|
|
1159
|
+
const autoResonance = input.autoResonance !== false;
|
|
1160
|
+
let modifiedFiles = input.modifiedFiles ? [...input.modifiedFiles] : [];
|
|
1161
|
+
let gitBranch;
|
|
1162
|
+
let gitCommit;
|
|
1163
|
+
if (autoGit) {
|
|
1164
|
+
const [detectedFiles, gitHead] = await Promise.all([
|
|
1165
|
+
modifiedFiles.length === 0 ? detectModifiedFiles(cwd) : Promise.resolve(modifiedFiles),
|
|
1166
|
+
detectGitHeadInfo(cwd)
|
|
1167
|
+
]);
|
|
1168
|
+
modifiedFiles = detectedFiles;
|
|
1169
|
+
gitBranch = gitHead.branch;
|
|
1170
|
+
gitCommit = gitHead.commit;
|
|
1171
|
+
}
|
|
1172
|
+
const activeFiles = input.activeFiles ? Array.from(new Set(input.activeFiles)) : modifiedFiles.slice(0, 15);
|
|
1173
|
+
const invariantsSet = new Set(input.activeInvariants || []);
|
|
1174
|
+
if (autoResonance) {
|
|
1175
|
+
try {
|
|
1176
|
+
const memoryStore = new CausalMemoryStore(this.memoryDbPath);
|
|
1177
|
+
const resonance = memoryStore.queryResonance({
|
|
1178
|
+
files: activeFiles.length > 0 ? activeFiles : modifiedFiles,
|
|
1179
|
+
keywords: input.keywords,
|
|
1180
|
+
domain: input.domain,
|
|
1181
|
+
propagateRelations: false
|
|
1182
|
+
});
|
|
1183
|
+
for (const rule of resonance.resonanceRules) {
|
|
1184
|
+
for (const inv of rule.invariants) {
|
|
1185
|
+
invariantsSet.add(inv);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
} catch {}
|
|
1189
|
+
}
|
|
1190
|
+
const checkpointId = input.id?.trim() || ContextCheckpointStore.generateCheckpointId();
|
|
1191
|
+
const nowIso = new Date().toISOString();
|
|
1192
|
+
const checkpoint = {
|
|
1193
|
+
id: checkpointId,
|
|
1194
|
+
sessionId,
|
|
1195
|
+
title: input.title.trim(),
|
|
1196
|
+
createdAt: nowIso,
|
|
1197
|
+
cwd: cwd.replaceAll("\\", "/"),
|
|
1198
|
+
gitBranch,
|
|
1199
|
+
gitCommit,
|
|
1200
|
+
modifiedFiles,
|
|
1201
|
+
activeFiles,
|
|
1202
|
+
goal: (input.goal || input.title).trim(),
|
|
1203
|
+
completedSteps: input.completedSteps ? [...input.completedSteps] : [],
|
|
1204
|
+
pendingTasks: input.pendingTasks ? [...input.pendingTasks] : [],
|
|
1205
|
+
keyDecisions: input.keyDecisions ? [...input.keyDecisions] : [],
|
|
1206
|
+
activeInvariants: Array.from(invariantsSet),
|
|
1207
|
+
artifacts: input.artifacts ? [...input.artifacts] : [],
|
|
1208
|
+
customMeta: input.customMeta
|
|
1209
|
+
};
|
|
1210
|
+
const filtered = this.database.checkpoints.filter((c) => c.id !== checkpoint.id);
|
|
1211
|
+
const maxRetained = Math.max(1, CAUSAL_MEMORY_CONFIG.MAX_CHECKPOINTS_RETAINED);
|
|
1212
|
+
const updatedList = [checkpoint, ...filtered].slice(0, maxRetained);
|
|
1213
|
+
this.database = {
|
|
1214
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1215
|
+
updatedAt: nowIso,
|
|
1216
|
+
latestCheckpointId: checkpoint.id,
|
|
1217
|
+
totalCheckpoints: updatedList.length,
|
|
1218
|
+
checkpoints: updatedList
|
|
1219
|
+
};
|
|
1220
|
+
this.saveDatabase();
|
|
1221
|
+
if (autoResonance) {
|
|
1222
|
+
try {
|
|
1223
|
+
const memoryStore = new CausalMemoryStore(this.memoryDbPath);
|
|
1224
|
+
memoryStore.assertRule({
|
|
1225
|
+
id: "ctx-active-session-snapshot",
|
|
1226
|
+
domain: "User_Preferences_And_Rules",
|
|
1227
|
+
title: "Active_Session_Context_Snapshot",
|
|
1228
|
+
entityType: "ContextCheckpoint",
|
|
1229
|
+
triggers: {
|
|
1230
|
+
alwaysActive: true,
|
|
1231
|
+
keywords: ["context", "checkpoint", "resume", "\uCEE8\uD14D\uC2A4\uD2B8", "\uC774\uC5B4\uC11C", checkpoint.sessionId]
|
|
1232
|
+
},
|
|
1233
|
+
invariants: [
|
|
1234
|
+
`[\uCD5C\uADFC \uC791\uC5C5 \uBAA9\uD45C] ${checkpoint.goal}`,
|
|
1235
|
+
...checkpoint.pendingTasks.length > 0 ? [`[\uB2E4\uC74C \uCD5C\uC6B0\uC120 \uC9C4\uD589 \uACFC\uC81C] ${checkpoint.pendingTasks[0]}`] : []
|
|
1236
|
+
],
|
|
1237
|
+
actions: checkpoint.pendingTasks.slice(0, 6),
|
|
1238
|
+
context: [
|
|
1239
|
+
`\uCCB4\uD06C\uD3EC\uC778\uD2B8 ID: ${checkpoint.id} (\uC138\uC158: ${checkpoint.sessionId}, \uC2DC\uAC01: ${checkpoint.createdAt})`,
|
|
1240
|
+
`\uC81C\uBAA9: ${checkpoint.title}`,
|
|
1241
|
+
...checkpoint.completedSteps.length > 0 ? [`\uC644\uB8CC \uB0B4\uC5ED: ${checkpoint.completedSteps.join(" | ")}`] : [],
|
|
1242
|
+
...checkpoint.keyDecisions.length > 0 ? [`\uD575\uC2EC \uACB0\uC815: ${checkpoint.keyDecisions.join(" | ")}`] : [],
|
|
1243
|
+
...checkpoint.artifacts.length > 0 ? [`\uC124\uACC4 \uC544\uD2F0\uD329\uD2B8: ${checkpoint.artifacts.join(", ")}`] : []
|
|
1244
|
+
]
|
|
1245
|
+
});
|
|
1246
|
+
} catch {}
|
|
1247
|
+
}
|
|
1248
|
+
return checkpoint;
|
|
1249
|
+
}
|
|
1250
|
+
getLatestCheckpoint(sessionId) {
|
|
1251
|
+
this.reloadIfNeeded();
|
|
1252
|
+
if (this.database.checkpoints.length === 0) {
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1255
|
+
if (sessionId && sessionId.trim().length > 0) {
|
|
1256
|
+
const targetSession = sessionId.trim().toLowerCase();
|
|
1257
|
+
return this.database.checkpoints.find((c) => c.sessionId.toLowerCase() === targetSession) || null;
|
|
1258
|
+
}
|
|
1259
|
+
return this.database.checkpoints[0] || null;
|
|
1260
|
+
}
|
|
1261
|
+
getCheckpoint(idOrTag, sessionId) {
|
|
1262
|
+
this.reloadIfNeeded();
|
|
1263
|
+
if (!idOrTag || idOrTag === "latest") {
|
|
1264
|
+
return this.getLatestCheckpoint(sessionId);
|
|
1265
|
+
}
|
|
1266
|
+
const cleanId = idOrTag.trim();
|
|
1267
|
+
const exact = this.database.checkpoints.find((c) => c.id === cleanId);
|
|
1268
|
+
if (exact)
|
|
1269
|
+
return exact;
|
|
1270
|
+
const cleanLower = cleanId.toLowerCase();
|
|
1271
|
+
return this.database.checkpoints.find((c) => c.id.toLowerCase().includes(cleanLower) || c.title.toLowerCase().includes(cleanLower) || c.sessionId.toLowerCase() === cleanLower) || null;
|
|
1272
|
+
}
|
|
1273
|
+
listCheckpoints(options) {
|
|
1274
|
+
this.reloadIfNeeded();
|
|
1275
|
+
let list = [...this.database.checkpoints];
|
|
1276
|
+
if (options?.sessionId && options.sessionId.trim().length > 0) {
|
|
1277
|
+
const target = options.sessionId.trim().toLowerCase();
|
|
1278
|
+
list = list.filter((c) => c.sessionId.toLowerCase() === target);
|
|
1279
|
+
}
|
|
1280
|
+
const limit = options?.limit && options.limit > 0 ? options.limit : list.length;
|
|
1281
|
+
return list.slice(0, limit);
|
|
1282
|
+
}
|
|
1283
|
+
deleteCheckpoint(id) {
|
|
1284
|
+
this.reloadIfNeeded();
|
|
1285
|
+
const beforeLen = this.database.checkpoints.length;
|
|
1286
|
+
const remaining = this.database.checkpoints.filter((c) => c.id !== id);
|
|
1287
|
+
if (remaining.length === beforeLen) {
|
|
1288
|
+
return false;
|
|
1289
|
+
}
|
|
1290
|
+
this.database = {
|
|
1291
|
+
...this.database,
|
|
1292
|
+
updatedAt: new Date().toISOString(),
|
|
1293
|
+
latestCheckpointId: remaining[0]?.id || null,
|
|
1294
|
+
totalCheckpoints: remaining.length,
|
|
1295
|
+
checkpoints: remaining
|
|
1296
|
+
};
|
|
1297
|
+
this.saveDatabase();
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
static formatForAgentResume(checkpoint, compact = false) {
|
|
1301
|
+
if (compact) {
|
|
1302
|
+
return JSON.stringify({
|
|
1303
|
+
ok: true,
|
|
1304
|
+
id: checkpoint.id,
|
|
1305
|
+
sessionId: checkpoint.sessionId,
|
|
1306
|
+
title: checkpoint.title,
|
|
1307
|
+
createdAt: checkpoint.createdAt,
|
|
1308
|
+
cwd: checkpoint.cwd,
|
|
1309
|
+
git: checkpoint.gitBranch ? `${checkpoint.gitBranch}@${checkpoint.gitCommit || "HEAD"}` : undefined,
|
|
1310
|
+
goal: checkpoint.goal,
|
|
1311
|
+
completedSteps: checkpoint.completedSteps,
|
|
1312
|
+
pendingTasks: checkpoint.pendingTasks,
|
|
1313
|
+
keyDecisions: checkpoint.keyDecisions,
|
|
1314
|
+
activeFiles: checkpoint.activeFiles,
|
|
1315
|
+
artifacts: checkpoint.artifacts,
|
|
1316
|
+
activeInvariants: checkpoint.activeInvariants
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
const lines = [
|
|
1320
|
+
`### \uD83D\uDCCC [Synapse Context Snapshot \uBCF5\uC6D0] ${checkpoint.title}`,
|
|
1321
|
+
`- **\uCCB4\uD06C\uD3EC\uC778\uD2B8 ID**: \`${checkpoint.id}\` (\uC138\uC158: \`${checkpoint.sessionId}\`, \uC2DC\uAC01: ${checkpoint.createdAt})`,
|
|
1322
|
+
`- **\uC791\uC5C5 \uACBD\uB85C**: \`${checkpoint.cwd}\`${checkpoint.gitBranch ? ` (Git: \`${checkpoint.gitBranch}\` @ \`${checkpoint.gitCommit || "HEAD"}\`)` : ""}`,
|
|
1323
|
+
`- **\uD575\uC2EC \uBAA9\uD45C(Goal)**: ${checkpoint.goal}`
|
|
1324
|
+
];
|
|
1325
|
+
if (checkpoint.completedSteps.length > 0) {
|
|
1326
|
+
lines.push(`
|
|
1327
|
+
#### \u2705 \uC644\uB8CC\uB41C \uC791\uC5C5 (Completed Steps)`);
|
|
1328
|
+
for (const step of checkpoint.completedSteps) {
|
|
1329
|
+
lines.push(` - ${step}`);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
if (checkpoint.pendingTasks.length > 0) {
|
|
1333
|
+
lines.push(`
|
|
1334
|
+
#### \u23F3 \uBC14\uB85C \uC774\uC5B4\uC11C \uC9C4\uD589\uD560 \uC791\uC5C5 (Next Pending Tasks)`);
|
|
1335
|
+
for (let i = 0;i < checkpoint.pendingTasks.length; i++) {
|
|
1336
|
+
lines.push(` ${i + 1}. **${checkpoint.pendingTasks[i]}**`);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
if (checkpoint.keyDecisions.length > 0) {
|
|
1340
|
+
lines.push(`
|
|
1341
|
+
#### \uD83D\uDCA1 \uD575\uC2EC \uC124\uACC4 \uACB0\uC815 \uBC0F \uB9E5\uB77D (Key Decisions)`);
|
|
1342
|
+
for (const dec of checkpoint.keyDecisions) {
|
|
1343
|
+
lines.push(` - ${dec}`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
if (checkpoint.artifacts.length > 0) {
|
|
1347
|
+
lines.push(`
|
|
1348
|
+
#### \uD83D\uDCC4 \uCC38\uC870 \uC544\uD2F0\uD329\uD2B8 \uBC0F \uC124\uACC4 \uBB38\uC11C (Artifacts)`);
|
|
1349
|
+
for (const art of checkpoint.artifacts) {
|
|
1350
|
+
lines.push(` - \`${art}\``);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (checkpoint.activeFiles.length > 0) {
|
|
1354
|
+
lines.push(`
|
|
1355
|
+
#### \uD83D\uDCC2 \uD575\uC2EC \uC791\uC5C5 \uD30C\uC77C (Active Files)`);
|
|
1356
|
+
lines.push(` - ${checkpoint.activeFiles.map((f) => `\`${f}\``).join(", ")}`);
|
|
1357
|
+
}
|
|
1358
|
+
if (checkpoint.activeInvariants.length > 0) {
|
|
1359
|
+
lines.push(`
|
|
1360
|
+
#### \uD83D\uDEE1\uFE0F \uD65C\uC131 \uBD88\uBCC0 \uADDC\uCE59 (Active Invariants)`);
|
|
1361
|
+
for (const inv of checkpoint.activeInvariants) {
|
|
1362
|
+
lines.push(` - ${inv}`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return lines.join(`
|
|
1366
|
+
`);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
var init_causal_context = __esm(() => {
|
|
1370
|
+
init_causal_config();
|
|
1371
|
+
init_causal_store();
|
|
1372
|
+
init_causal_query();
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
// causal-timeseries.ts
|
|
1376
|
+
class CausalTimeSeriesEngine {
|
|
1377
|
+
contextStore;
|
|
1378
|
+
constructor(contextStore, _memoryStore) {
|
|
1379
|
+
this.contextStore = contextStore || new ContextCheckpointStore;
|
|
1380
|
+
}
|
|
1381
|
+
static calculateTemporalDecayScore(options) {
|
|
1382
|
+
const baseScore = options.baseScore ?? 1;
|
|
1383
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
1384
|
+
const deltaHours = Math.max(0, (nowMs - options.lastActiveMs) / (1000 * 60 * 60));
|
|
1385
|
+
const halfLifeHours = Math.max(0.1, options.halfLifeHours ?? 24);
|
|
1386
|
+
const lambda = Math.LN2 / halfLifeHours;
|
|
1387
|
+
const hitCount = Math.max(0, options.hitCount ?? 0);
|
|
1388
|
+
const decayFactor = Math.exp(-lambda * deltaHours);
|
|
1389
|
+
const frequencyBoost = 0.25 * Math.log(1 + hitCount);
|
|
1390
|
+
return Number((baseScore * decayFactor + frequencyBoost).toFixed(4));
|
|
1391
|
+
}
|
|
1392
|
+
computeDecayedResonance(rules) {
|
|
1393
|
+
return rules.map((r) => {
|
|
1394
|
+
const lastActiveMs = r.updatedAt ? new Date(r.updatedAt).getTime() : Date.now();
|
|
1395
|
+
const decayScore = CausalTimeSeriesEngine.calculateTemporalDecayScore({
|
|
1396
|
+
baseScore: (r.confidence ?? 1) * (r.weight ?? 1),
|
|
1397
|
+
lastActiveMs
|
|
1398
|
+
});
|
|
1399
|
+
return { ...r, decayScore };
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
generateTimeline(options) {
|
|
1403
|
+
const opt = typeof options === "number" ? { limit: options } : options;
|
|
1404
|
+
const checkpoints = this.contextStore.listCheckpoints({
|
|
1405
|
+
sessionId: opt?.sessionId
|
|
1406
|
+
});
|
|
1407
|
+
const nowMs = Date.now();
|
|
1408
|
+
const sinceMs = opt?.sinceMs ?? nowMs - 7 * 24 * 60 * 60 * 1000;
|
|
1409
|
+
const events = [];
|
|
1410
|
+
for (const cp of checkpoints) {
|
|
1411
|
+
const tsMs = new Date(cp.createdAt).getTime();
|
|
1412
|
+
if (tsMs < sinceMs)
|
|
1413
|
+
continue;
|
|
1414
|
+
events.push({
|
|
1415
|
+
id: `tl_${cp.id}`,
|
|
1416
|
+
timestamp: cp.createdAt,
|
|
1417
|
+
timestampMs: tsMs,
|
|
1418
|
+
type: "checkpoint",
|
|
1419
|
+
sessionId: cp.sessionId,
|
|
1420
|
+
title: cp.title,
|
|
1421
|
+
summary: `\uBAA9\uD45C: ${cp.goal} | \uC644\uB8CC: ${cp.completedSteps.length}\uAC74 | \uC794\uC5EC: ${cp.pendingTasks.length}\uAC74`,
|
|
1422
|
+
tags: [cp.sessionId, cp.gitBranch || "main"],
|
|
1423
|
+
checkpointId: cp.id
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
events.sort((a, b) => b.timestampMs - a.timestampMs);
|
|
1427
|
+
const limit = opt?.limit && opt.limit > 0 ? opt.limit : events.length;
|
|
1428
|
+
return events.slice(0, limit);
|
|
1429
|
+
}
|
|
1430
|
+
diffCheckpoints(fromId, toId) {
|
|
1431
|
+
const fromCp = this.contextStore.getCheckpoint(fromId);
|
|
1432
|
+
const toCp = this.contextStore.getCheckpoint(toId);
|
|
1433
|
+
if (!fromCp) {
|
|
1434
|
+
throw new Error(`\uC2DC\uC791 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${fromId}`);
|
|
1435
|
+
}
|
|
1436
|
+
if (!toCp) {
|
|
1437
|
+
throw new Error(`\uBE44\uAD50 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${toId}`);
|
|
1438
|
+
}
|
|
1439
|
+
const fromMs = new Date(fromCp.createdAt).getTime();
|
|
1440
|
+
const toMs = new Date(toCp.createdAt).getTime();
|
|
1441
|
+
const deltaHours = Number(Math.abs(toMs - fromMs) / (1000 * 60 * 60)).toFixed(2);
|
|
1442
|
+
const fromPendingSet = new Set(fromCp.pendingTasks);
|
|
1443
|
+
const toPendingSet = new Set(toCp.pendingTasks);
|
|
1444
|
+
const addedPendingTasks = toCp.pendingTasks.filter((t) => !fromPendingSet.has(t));
|
|
1445
|
+
const resolvedTasks = fromCp.pendingTasks.filter((t) => !toPendingSet.has(t));
|
|
1446
|
+
const fromCompletedSet = new Set(fromCp.completedSteps);
|
|
1447
|
+
const newCompletedSteps = toCp.completedSteps.filter((s) => !fromCompletedSet.has(s));
|
|
1448
|
+
const fromDecisionsSet = new Set(fromCp.keyDecisions);
|
|
1449
|
+
const newDecisions = toCp.keyDecisions.filter((d) => !fromDecisionsSet.has(d));
|
|
1450
|
+
const fromFilesSet = new Set(fromCp.modifiedFiles);
|
|
1451
|
+
const toFilesSet = new Set(toCp.modifiedFiles);
|
|
1452
|
+
const addedFiles = toCp.modifiedFiles.filter((f) => !fromFilesSet.has(f));
|
|
1453
|
+
const removedFiles = fromCp.modifiedFiles.filter((f) => !toFilesSet.has(f));
|
|
1454
|
+
const retainedFiles = toCp.modifiedFiles.filter((f) => fromFilesSet.has(f));
|
|
1455
|
+
const fromInvSet = new Set(fromCp.activeInvariants);
|
|
1456
|
+
const toInvSet = new Set(toCp.activeInvariants);
|
|
1457
|
+
const addedInvariants = toCp.activeInvariants.filter((i) => !fromInvSet.has(i));
|
|
1458
|
+
const removedInvariants = fromCp.activeInvariants.filter((i) => !toInvSet.has(i));
|
|
1459
|
+
return {
|
|
1460
|
+
fromId: fromCp.id,
|
|
1461
|
+
toId: toCp.id,
|
|
1462
|
+
fromCheckpointId: fromCp.id,
|
|
1463
|
+
toCheckpointId: toCp.id,
|
|
1464
|
+
fromTimestamp: fromCp.createdAt,
|
|
1465
|
+
toTimestamp: toCp.createdAt,
|
|
1466
|
+
timeDeltaHours: parseFloat(deltaHours),
|
|
1467
|
+
newlyCompletedSteps: newCompletedSteps,
|
|
1468
|
+
resolvedPendingTasks: resolvedTasks,
|
|
1469
|
+
addedModifiedFiles: addedFiles,
|
|
1470
|
+
addedPendingTasks,
|
|
1471
|
+
resolvedTasks,
|
|
1472
|
+
newCompletedSteps,
|
|
1473
|
+
newDecisions,
|
|
1474
|
+
modifiedFilesDelta: {
|
|
1475
|
+
added: addedFiles,
|
|
1476
|
+
removed: removedFiles,
|
|
1477
|
+
retained: retainedFiles
|
|
1478
|
+
},
|
|
1479
|
+
activeInvariantsDelta: {
|
|
1480
|
+
added: addedInvariants,
|
|
1481
|
+
removed: removedInvariants
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
async rewindToCheckpoint(targetIdOrTag) {
|
|
1486
|
+
const targetCp = this.contextStore.getCheckpoint(targetIdOrTag);
|
|
1487
|
+
if (!targetCp) {
|
|
1488
|
+
throw new Error(`\uB418\uAC10\uAE30 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetIdOrTag}`);
|
|
1489
|
+
}
|
|
1490
|
+
const rolledBack = await this.contextStore.saveCheckpoint({
|
|
1491
|
+
sessionId: targetCp.sessionId,
|
|
1492
|
+
title: `[Rewind \uB864\uBC31] ${targetCp.title}`,
|
|
1493
|
+
goal: targetCp.goal,
|
|
1494
|
+
cwd: targetCp.cwd,
|
|
1495
|
+
activeFiles: targetCp.activeFiles,
|
|
1496
|
+
modifiedFiles: targetCp.modifiedFiles,
|
|
1497
|
+
completedSteps: targetCp.completedSteps,
|
|
1498
|
+
pendingTasks: targetCp.pendingTasks,
|
|
1499
|
+
keyDecisions: [
|
|
1500
|
+
`\uACFC\uAC70 \uCCB4\uD06C\uD3EC\uC778\uD2B8(${targetCp.id}, ${targetCp.createdAt})\uB85C \uC2DC\uACC4\uC5F4 \uB418\uAC10\uAE30 \uC218\uD589\uB428`,
|
|
1501
|
+
...targetCp.keyDecisions
|
|
1502
|
+
],
|
|
1503
|
+
activeInvariants: targetCp.activeInvariants,
|
|
1504
|
+
artifacts: targetCp.artifacts,
|
|
1505
|
+
autoGit: false,
|
|
1506
|
+
autoResonance: false
|
|
1507
|
+
});
|
|
1508
|
+
return rolledBack;
|
|
1509
|
+
}
|
|
1510
|
+
static formatDiffReport(diff) {
|
|
1511
|
+
const lines = [
|
|
1512
|
+
`### \u23F3 [Synapse \uC2DC\uACC4\uC5F4 \uCC28\uBD84 \uBD84\uC11D] \`${diff.fromCheckpointId}\` \u2794 \`${diff.toCheckpointId}\``,
|
|
1513
|
+
`- **\uACBD\uACFC \uC2DC\uAC04**: \uC57D ${diff.timeDeltaHours}\uC2DC\uAC04 (${diff.fromTimestamp} \u2794 ${diff.toTimestamp})`
|
|
1514
|
+
];
|
|
1515
|
+
if (diff.resolvedTasks.length > 0) {
|
|
1516
|
+
lines.push(`
|
|
1517
|
+
#### \uD83C\uDFAF \uD574\uACB0/\uC644\uB8CC\uB41C \uACFC\uC81C (Resolved Tasks)`);
|
|
1518
|
+
for (const t of diff.resolvedTasks)
|
|
1519
|
+
lines.push(` - \u2714 ~~${t}~~`);
|
|
1520
|
+
}
|
|
1521
|
+
if (diff.newCompletedSteps.length > 0) {
|
|
1522
|
+
lines.push(`
|
|
1523
|
+
#### \u2728 \uC0C8\uB85C \uC644\uB8CC\uB41C \uB2E8\uACC4 (New Completed Steps)`);
|
|
1524
|
+
for (const s of diff.newCompletedSteps)
|
|
1525
|
+
lines.push(` - \u2705 ${s}`);
|
|
1526
|
+
}
|
|
1527
|
+
if (diff.addedPendingTasks.length > 0) {
|
|
1528
|
+
lines.push(`
|
|
1529
|
+
#### \u23F3 \uC0C8\uB85C \uBD80\uC5EC\uB41C \uB2E4\uC74C \uACFC\uC81C (Added Pending Tasks)`);
|
|
1530
|
+
for (const t of diff.addedPendingTasks)
|
|
1531
|
+
lines.push(` - \uD83D\uDCCC **${t}**`);
|
|
1532
|
+
}
|
|
1533
|
+
if (diff.newDecisions.length > 0) {
|
|
1534
|
+
lines.push(`
|
|
1535
|
+
#### \uD83D\uDCA1 \uC0C8\uB85C\uC6B4 \uC124\uACC4 \uACB0\uC815 (New Key Decisions)`);
|
|
1536
|
+
for (const d of diff.newDecisions)
|
|
1537
|
+
lines.push(` - ${d}`);
|
|
1538
|
+
}
|
|
1539
|
+
if (diff.modifiedFilesDelta.added.length > 0 || diff.modifiedFilesDelta.removed.length > 0) {
|
|
1540
|
+
lines.push(`
|
|
1541
|
+
#### \uD83D\uDCC2 \uD30C\uC77C \uBCC0\uACBD\uC810 (Files Delta)`);
|
|
1542
|
+
if (diff.modifiedFilesDelta.added.length > 0) {
|
|
1543
|
+
lines.push(` - \uD83D\uDFE2 **\uCD94\uAC00**: ${diff.modifiedFilesDelta.added.map((f) => `\`${f}\``).join(", ")}`);
|
|
1544
|
+
}
|
|
1545
|
+
if (diff.modifiedFilesDelta.removed.length > 0) {
|
|
1546
|
+
lines.push(` - \uD83D\uDD34 **\uC81C\uAC70**: ${diff.modifiedFilesDelta.removed.map((f) => `\`${f}\``).join(", ")}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
if (diff.activeInvariantsDelta.added.length > 0) {
|
|
1550
|
+
lines.push(`
|
|
1551
|
+
#### \uD83D\uDEE1\uFE0F \uC0C8\uB85C \uBC1C\uB3D9\uB41C \uBD88\uBCC0 \uADDC\uCE59 (New Invariants)`);
|
|
1552
|
+
for (const i of diff.activeInvariantsDelta.added)
|
|
1553
|
+
lines.push(` - ${i}`);
|
|
1554
|
+
}
|
|
1555
|
+
return lines.join(`
|
|
1556
|
+
`);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
var init_causal_timeseries = __esm(() => {
|
|
1560
|
+
init_causal_context();
|
|
1561
|
+
});
|
|
1562
|
+
|
|
1563
|
+
// causal-graveyard.ts
|
|
1564
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, renameSync as renameSync2, statSync as statSync3 } from "fs";
|
|
1565
|
+
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
1566
|
+
import { homedir as homedir2 } from "os";
|
|
1567
|
+
|
|
1568
|
+
class CausalGraveyardStore {
|
|
1569
|
+
dbPath;
|
|
1570
|
+
database;
|
|
1571
|
+
lastMtimeMs = 0;
|
|
1572
|
+
constructor(customPathOrMemoryStore) {
|
|
1573
|
+
const customPath = typeof customPathOrMemoryStore === "string" ? customPathOrMemoryStore : undefined;
|
|
1574
|
+
this.dbPath = customPath || process.env.SYNAPSE_GRAVEYARD_PATH || resolve2(homedir2(), ".gemini", "antigravity", "causal_graveyard.json");
|
|
1575
|
+
this.database = this.loadDatabase();
|
|
1576
|
+
}
|
|
1577
|
+
loadDatabase() {
|
|
1578
|
+
try {
|
|
1579
|
+
if (existsSync3(this.dbPath)) {
|
|
1580
|
+
const stat = statSync3(this.dbPath);
|
|
1581
|
+
this.lastMtimeMs = stat.mtimeMs;
|
|
1582
|
+
const raw = readFileSync3(this.dbPath, "utf-8");
|
|
1583
|
+
const parsed = JSON.parse(raw);
|
|
1584
|
+
if (parsed && Array.isArray(parsed.traps)) {
|
|
1585
|
+
return parsed;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
} catch {}
|
|
1589
|
+
return {
|
|
1590
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1591
|
+
updatedAt: new Date().toISOString(),
|
|
1592
|
+
totalTraps: 0,
|
|
1593
|
+
traps: []
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
reloadIfNeeded() {
|
|
1597
|
+
try {
|
|
1598
|
+
if (existsSync3(this.dbPath)) {
|
|
1599
|
+
const stat = statSync3(this.dbPath);
|
|
1600
|
+
if (stat.mtimeMs > this.lastMtimeMs) {
|
|
1601
|
+
this.database = this.loadDatabase();
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
} catch {}
|
|
1605
|
+
}
|
|
1606
|
+
saveDatabase() {
|
|
1607
|
+
try {
|
|
1608
|
+
const dir = dirname3(this.dbPath);
|
|
1609
|
+
if (!existsSync3(dir)) {
|
|
1610
|
+
mkdirSync3(dir, { recursive: true });
|
|
1611
|
+
}
|
|
1612
|
+
const serialized = JSON.stringify(this.database, null, 2);
|
|
1613
|
+
const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
|
|
1614
|
+
writeFileSync3(tempPath, serialized, "utf-8");
|
|
1615
|
+
renameSync2(tempPath, this.dbPath);
|
|
1616
|
+
try {
|
|
1617
|
+
this.lastMtimeMs = statSync3(this.dbPath).mtimeMs;
|
|
1618
|
+
} catch {}
|
|
1619
|
+
} catch (error) {
|
|
1620
|
+
throw new Error(`\uC2E4\uD328 \uAC00\uC124 \uBB18\uC9C0 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
assertTrap(input) {
|
|
1624
|
+
this.reloadIfNeeded();
|
|
1625
|
+
const nowIso = new Date().toISOString();
|
|
1626
|
+
const id = input.id || `trap_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
1627
|
+
const title = input.title || input.trapPattern || "\uC54C \uC218 \uC5C6\uB294 \uD568\uC815 \uD328\uD134";
|
|
1628
|
+
const failedApproach = input.failedApproach || input.trapPattern || title;
|
|
1629
|
+
const rootCause = input.rootCause || input.reason || "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
|
|
1630
|
+
const counterMeasure = input.counterMeasure || input.safeAlternative || "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
|
|
1631
|
+
const triggerKeywords = Array.isArray(input.triggerKeywords) ? input.triggerKeywords : Array.isArray(input.keywords) ? input.keywords : title.split(/\s+/).filter(Boolean);
|
|
1632
|
+
const triggerFiles = Array.isArray(input.triggerFiles) ? input.triggerFiles : Array.isArray(input.avoidFiles) ? input.avoidFiles : Array.isArray(input.files) ? input.files : undefined;
|
|
1633
|
+
const trap = {
|
|
1634
|
+
id,
|
|
1635
|
+
title,
|
|
1636
|
+
failedApproach,
|
|
1637
|
+
rootCause,
|
|
1638
|
+
counterMeasure,
|
|
1639
|
+
triggerKeywords,
|
|
1640
|
+
triggerFiles,
|
|
1641
|
+
errorSignature: input.errorSignature,
|
|
1642
|
+
environment: input.environment || input.domain || "general",
|
|
1643
|
+
hitCount: (input.hitCount ?? 0) + 1,
|
|
1644
|
+
createdAt: input.createdAt || nowIso,
|
|
1645
|
+
lastEncounteredAt: nowIso,
|
|
1646
|
+
trapPattern: failedApproach,
|
|
1647
|
+
domain: input.domain || input.environment || "general",
|
|
1648
|
+
severity: input.severity || (input.errorSignature ? "CRITICAL" : "WARNING"),
|
|
1649
|
+
reason: rootCause,
|
|
1650
|
+
safeAlternative: counterMeasure,
|
|
1651
|
+
avoidFiles: triggerFiles
|
|
1652
|
+
};
|
|
1653
|
+
const filtered = this.database.traps.filter((t) => t.id !== trap.id);
|
|
1654
|
+
const updated = [trap, ...filtered];
|
|
1655
|
+
this.database = {
|
|
1656
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1657
|
+
updatedAt: nowIso,
|
|
1658
|
+
totalTraps: updated.length,
|
|
1659
|
+
traps: updated
|
|
1660
|
+
};
|
|
1661
|
+
this.saveDatabase();
|
|
1662
|
+
return trap;
|
|
1663
|
+
}
|
|
1664
|
+
scanTraps(query) {
|
|
1665
|
+
this.reloadIfNeeded();
|
|
1666
|
+
const warnings = [];
|
|
1667
|
+
const fullText = [
|
|
1668
|
+
query.text || "",
|
|
1669
|
+
query.code || "",
|
|
1670
|
+
...query.keywords || []
|
|
1671
|
+
].join(" ").toLowerCase();
|
|
1672
|
+
const files = (query.files || []).map((f) => f.replaceAll("\\", "/").toLowerCase());
|
|
1673
|
+
for (const trap of this.database.traps) {
|
|
1674
|
+
let matched = false;
|
|
1675
|
+
let matchedBy = "keyword";
|
|
1676
|
+
let matchedPattern = "";
|
|
1677
|
+
if (trap.errorSignature && trap.errorSignature.length >= 3) {
|
|
1678
|
+
const sigLower = trap.errorSignature.toLowerCase();
|
|
1679
|
+
if (fullText.includes(sigLower)) {
|
|
1680
|
+
matched = true;
|
|
1681
|
+
matchedBy = "error_signature";
|
|
1682
|
+
matchedPattern = trap.errorSignature;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
const targetFiles = trap.triggerFiles || trap.avoidFiles || [];
|
|
1686
|
+
if (!matched && targetFiles.length > 0) {
|
|
1687
|
+
for (const tf of targetFiles) {
|
|
1688
|
+
const tfLower = tf.replaceAll("\\", "/").toLowerCase();
|
|
1689
|
+
if (files.some((f) => f.includes(tfLower) || tfLower.includes(f))) {
|
|
1690
|
+
matched = true;
|
|
1691
|
+
matchedBy = "file";
|
|
1692
|
+
matchedPattern = tf;
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
const targetKeywords = trap.triggerKeywords || [];
|
|
1698
|
+
if (!matched && targetKeywords.length > 0) {
|
|
1699
|
+
for (const kw of targetKeywords) {
|
|
1700
|
+
const kwLower = kw.toLowerCase();
|
|
1701
|
+
if (kwLower.length >= 2 && fullText.includes(kwLower)) {
|
|
1702
|
+
matched = true;
|
|
1703
|
+
matchedBy = "keyword";
|
|
1704
|
+
matchedPattern = kw;
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
if (matched) {
|
|
1710
|
+
warnings.push({
|
|
1711
|
+
trapId: trap.id,
|
|
1712
|
+
title: trap.title,
|
|
1713
|
+
riskLevel: trap.severity === "CRITICAL" || trap.errorSignature ? "CRITICAL" : "WARNING",
|
|
1714
|
+
matchedBy,
|
|
1715
|
+
matchedPattern,
|
|
1716
|
+
whyItFails: trap.rootCause,
|
|
1717
|
+
recommendedFix: trap.counterMeasure
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
const hasCritical = warnings.some((w) => w.riskLevel === "CRITICAL");
|
|
1722
|
+
const highestSeverity = warnings.length === 0 ? "NONE" : hasCritical ? "CRITICAL" : "WARNING";
|
|
1723
|
+
const result = warnings;
|
|
1724
|
+
Object.assign(result, {
|
|
1725
|
+
trapped: warnings.length > 0,
|
|
1726
|
+
triggeredTraps: warnings,
|
|
1727
|
+
highestSeverity
|
|
1728
|
+
});
|
|
1729
|
+
return result;
|
|
1730
|
+
}
|
|
1731
|
+
listTraps() {
|
|
1732
|
+
this.reloadIfNeeded();
|
|
1733
|
+
return [...this.database.traps];
|
|
1734
|
+
}
|
|
1735
|
+
getAllTraps() {
|
|
1736
|
+
return this.listTraps();
|
|
1737
|
+
}
|
|
1738
|
+
deleteTrap(id) {
|
|
1739
|
+
this.reloadIfNeeded();
|
|
1740
|
+
const before = this.database.traps.length;
|
|
1741
|
+
const remaining = this.database.traps.filter((t) => t.id !== id);
|
|
1742
|
+
if (remaining.length === before)
|
|
1743
|
+
return false;
|
|
1744
|
+
this.database = {
|
|
1745
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1746
|
+
updatedAt: new Date().toISOString(),
|
|
1747
|
+
totalTraps: remaining.length,
|
|
1748
|
+
traps: remaining
|
|
1749
|
+
};
|
|
1750
|
+
this.saveDatabase();
|
|
1751
|
+
return true;
|
|
1752
|
+
}
|
|
1753
|
+
static formatWarningsForAgent(warnings) {
|
|
1754
|
+
if (!warnings || warnings.length === 0)
|
|
1755
|
+
return "";
|
|
1756
|
+
const lines = [
|
|
1757
|
+
`### \uD83D\uDC80 [\uC2E4\uD328 \uAC00\uC124 \uBB18\uC9C0 \uACBD\uACE0 (Graveyard Trap Detected)] \uC774\uBBF8 \uC2E4\uD328\uD588\uB358 \uC548\uD2F0\uD328\uD134 ${warnings.length}\uAC74 \uAC10\uC9C0!`
|
|
1758
|
+
];
|
|
1759
|
+
for (const w of warnings) {
|
|
1760
|
+
lines.push(`
|
|
1761
|
+
\uD83D\uDEA8 **[${w.riskLevel}] ${w.title}** (\uAC10\uC9C0: \`${w.matchedPattern}\` by ${w.matchedBy})`);
|
|
1762
|
+
lines.push(` - \u274C **\uC2E4\uD328 \uC6D0\uC778**: ${w.whyItFails}`);
|
|
1763
|
+
lines.push(` - \uD83D\uDCA1 **\uAC80\uC99D\uB41C \uB300\uC548**: ${w.recommendedFix}`);
|
|
1764
|
+
}
|
|
1765
|
+
return lines.join(`
|
|
1766
|
+
`);
|
|
1767
|
+
}
|
|
1768
|
+
static formatTrapWarnings(warnings) {
|
|
1769
|
+
if (!warnings)
|
|
1770
|
+
return "";
|
|
1771
|
+
if (Array.isArray(warnings))
|
|
1772
|
+
return CausalGraveyardStore.formatWarningsForAgent(warnings);
|
|
1773
|
+
if (warnings && Array.isArray(warnings.triggeredTraps)) {
|
|
1774
|
+
return CausalGraveyardStore.formatWarningsForAgent(warnings.triggeredTraps);
|
|
1775
|
+
}
|
|
1776
|
+
return "";
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
var init_causal_graveyard = __esm(() => {
|
|
1780
|
+
init_causal_config();
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
// causal-branch.ts
|
|
1784
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, renameSync as renameSync3, statSync as statSync4 } from "fs";
|
|
1785
|
+
import { dirname as dirname4, resolve as resolve3 } from "path";
|
|
1786
|
+
import { homedir as homedir3 } from "os";
|
|
1787
|
+
|
|
1788
|
+
class CausalBranchStore {
|
|
1789
|
+
dbPath;
|
|
1790
|
+
contextStore;
|
|
1791
|
+
graveyardStore;
|
|
1792
|
+
database;
|
|
1793
|
+
lastMtimeMs = 0;
|
|
1794
|
+
constructor(customPathOrContextStore, contextStoreOrGraveyard, graveyardStore) {
|
|
1795
|
+
if (typeof customPathOrContextStore === "string") {
|
|
1796
|
+
this.dbPath = customPathOrContextStore;
|
|
1797
|
+
this.contextStore = contextStoreOrGraveyard instanceof ContextCheckpointStore ? contextStoreOrGraveyard : new ContextCheckpointStore;
|
|
1798
|
+
this.graveyardStore = graveyardStore || (contextStoreOrGraveyard instanceof CausalGraveyardStore ? contextStoreOrGraveyard : new CausalGraveyardStore);
|
|
1799
|
+
} else if (customPathOrContextStore instanceof ContextCheckpointStore) {
|
|
1800
|
+
this.dbPath = process.env.SYNAPSE_BRANCH_PATH || resolve3(homedir3(), ".gemini", "antigravity", "causal_branches.json");
|
|
1801
|
+
this.contextStore = customPathOrContextStore;
|
|
1802
|
+
this.graveyardStore = contextStoreOrGraveyard instanceof CausalGraveyardStore ? contextStoreOrGraveyard : new CausalGraveyardStore;
|
|
1803
|
+
} else {
|
|
1804
|
+
this.dbPath = process.env.SYNAPSE_BRANCH_PATH || resolve3(homedir3(), ".gemini", "antigravity", "causal_branches.json");
|
|
1805
|
+
this.contextStore = new ContextCheckpointStore;
|
|
1806
|
+
this.graveyardStore = new CausalGraveyardStore;
|
|
1807
|
+
}
|
|
1808
|
+
this.database = this.loadDatabase();
|
|
1809
|
+
}
|
|
1810
|
+
loadDatabase() {
|
|
1811
|
+
try {
|
|
1812
|
+
if (existsSync4(this.dbPath)) {
|
|
1813
|
+
const stat = statSync4(this.dbPath);
|
|
1814
|
+
this.lastMtimeMs = stat.mtimeMs;
|
|
1815
|
+
const raw = readFileSync4(this.dbPath, "utf-8");
|
|
1816
|
+
const parsed = JSON.parse(raw);
|
|
1817
|
+
if (parsed && Array.isArray(parsed.branches)) {
|
|
1818
|
+
return parsed;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
} catch {}
|
|
1822
|
+
return {
|
|
1823
|
+
version: CAUSAL_MEMORY_CONFIG.SCHEMA_VERSION,
|
|
1824
|
+
updatedAt: new Date().toISOString(),
|
|
1825
|
+
currentBranch: "main",
|
|
1826
|
+
branches: [
|
|
1827
|
+
{
|
|
1828
|
+
name: "main",
|
|
1829
|
+
baseCheckpointId: "root",
|
|
1830
|
+
headCheckpointId: "root",
|
|
1831
|
+
createdAt: new Date().toISOString(),
|
|
1832
|
+
updatedAt: new Date().toISOString(),
|
|
1833
|
+
status: "active",
|
|
1834
|
+
description: "\uAE30\uBCF8 \uBA54\uC778 \uB450\uB1CC \uBE0C\uB79C\uCE58"
|
|
1835
|
+
}
|
|
1836
|
+
]
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
reloadIfNeeded() {
|
|
1840
|
+
try {
|
|
1841
|
+
if (existsSync4(this.dbPath)) {
|
|
1842
|
+
const stat = statSync4(this.dbPath);
|
|
1843
|
+
if (stat.mtimeMs > this.lastMtimeMs) {
|
|
1844
|
+
this.database = this.loadDatabase();
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
} catch {}
|
|
1848
|
+
}
|
|
1849
|
+
saveDatabase() {
|
|
1850
|
+
try {
|
|
1851
|
+
const dir = dirname4(this.dbPath);
|
|
1852
|
+
if (!existsSync4(dir)) {
|
|
1853
|
+
mkdirSync4(dir, { recursive: true });
|
|
1854
|
+
}
|
|
1855
|
+
const serialized = JSON.stringify(this.database, null, 2);
|
|
1856
|
+
const tempPath = `${this.dbPath}.tmp.${process.pid}.${Date.now()}`;
|
|
1857
|
+
writeFileSync4(tempPath, serialized, "utf-8");
|
|
1858
|
+
renameSync3(tempPath, this.dbPath);
|
|
1859
|
+
try {
|
|
1860
|
+
this.lastMtimeMs = statSync4(this.dbPath).mtimeMs;
|
|
1861
|
+
} catch {}
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
throw new Error(`\uCEE8\uD14D\uC2A4\uD2B8 \uBE0C\uB79C\uCE58 \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uC800\uC7A5 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
async createBranch(name, options) {
|
|
1867
|
+
this.reloadIfNeeded();
|
|
1868
|
+
const cleanName = name.trim();
|
|
1869
|
+
if (!cleanName)
|
|
1870
|
+
throw new Error("\uBE0C\uB79C\uCE58 \uC774\uB984\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1871
|
+
const existing = this.database.branches.find((b) => b.name === cleanName);
|
|
1872
|
+
if (existing && existing.status === "active") {
|
|
1873
|
+
throw new Error(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD65C\uC131 \uBE0C\uB79C\uCE58\uC785\uB2C8\uB2E4: ${cleanName}`);
|
|
1874
|
+
}
|
|
1875
|
+
let baseCp = null;
|
|
1876
|
+
if (options?.baseId) {
|
|
1877
|
+
baseCp = this.contextStore.getCheckpoint(options.baseId);
|
|
1878
|
+
} else {
|
|
1879
|
+
baseCp = this.contextStore.getLatestCheckpoint();
|
|
1880
|
+
}
|
|
1881
|
+
const baseId = baseCp?.id || "root";
|
|
1882
|
+
const nowIso = new Date().toISOString();
|
|
1883
|
+
const forkedCp = await this.contextStore.saveCheckpoint({
|
|
1884
|
+
sessionId: `branch-${cleanName}`,
|
|
1885
|
+
title: `[Branch Start] ${cleanName}`,
|
|
1886
|
+
goal: options?.description || `\uBE0C\uB79C\uCE58 ${cleanName} \uB3C5\uB9BD \uAC00\uC124 \uAC80\uC99D`,
|
|
1887
|
+
completedSteps: baseCp?.completedSteps || [],
|
|
1888
|
+
pendingTasks: baseCp?.pendingTasks || [],
|
|
1889
|
+
keyDecisions: baseCp ? [`\uBE0C\uB79C\uCE58 ${cleanName}\uC774 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ${baseId}\uC5D0\uC11C \uBD84\uAE30\uB428`] : [],
|
|
1890
|
+
activeFiles: baseCp?.activeFiles || [],
|
|
1891
|
+
artifacts: baseCp?.artifacts || [],
|
|
1892
|
+
activeInvariants: baseCp?.activeInvariants || [],
|
|
1893
|
+
autoGit: false,
|
|
1894
|
+
autoResonance: false
|
|
1895
|
+
});
|
|
1896
|
+
const newBranch = {
|
|
1897
|
+
name: cleanName,
|
|
1898
|
+
baseCheckpointId: baseId,
|
|
1899
|
+
headCheckpointId: forkedCp.id,
|
|
1900
|
+
createdAt: nowIso,
|
|
1901
|
+
updatedAt: nowIso,
|
|
1902
|
+
status: "active",
|
|
1903
|
+
description: options?.description
|
|
1904
|
+
};
|
|
1905
|
+
const remaining = this.database.branches.filter((b) => b.name !== cleanName);
|
|
1906
|
+
this.database = {
|
|
1907
|
+
...this.database,
|
|
1908
|
+
updatedAt: nowIso,
|
|
1909
|
+
currentBranch: cleanName,
|
|
1910
|
+
branches: [...remaining, newBranch]
|
|
1911
|
+
};
|
|
1912
|
+
this.saveDatabase();
|
|
1913
|
+
return newBranch;
|
|
1914
|
+
}
|
|
1915
|
+
switchBranch(name) {
|
|
1916
|
+
this.reloadIfNeeded();
|
|
1917
|
+
const clean = name.trim();
|
|
1918
|
+
const target = this.database.branches.find((b) => b.name === clean);
|
|
1919
|
+
if (!target) {
|
|
1920
|
+
throw new Error(`\uC874\uC7AC\uD558\uC9C0 \uC54A\uB294 \uBE0C\uB79C\uCE58\uC785\uB2C8\uB2E4: ${clean}`);
|
|
1921
|
+
}
|
|
1922
|
+
this.database = {
|
|
1923
|
+
...this.database,
|
|
1924
|
+
updatedAt: new Date().toISOString(),
|
|
1925
|
+
currentBranch: clean
|
|
1926
|
+
};
|
|
1927
|
+
this.saveDatabase();
|
|
1928
|
+
return target;
|
|
1929
|
+
}
|
|
1930
|
+
async mergeBranch(sourceName, targetName = "main") {
|
|
1931
|
+
this.reloadIfNeeded();
|
|
1932
|
+
const src = this.database.branches.find((b) => b.name === sourceName.trim());
|
|
1933
|
+
const tgt = this.database.branches.find((b) => b.name === targetName.trim());
|
|
1934
|
+
if (!src)
|
|
1935
|
+
throw new Error(`\uC18C\uC2A4 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${sourceName}`);
|
|
1936
|
+
if (!tgt)
|
|
1937
|
+
throw new Error(`\uD0C0\uAE43 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetName}`);
|
|
1938
|
+
if (src.status !== "active")
|
|
1939
|
+
throw new Error(`\uC18C\uC2A4 \uBE0C\uB79C\uCE58\uAC00 \uD65C\uC131 \uC0C1\uD0DC\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${src.status}`);
|
|
1940
|
+
const srcCp = this.contextStore.getCheckpoint(src.headCheckpointId);
|
|
1941
|
+
const tgtCp = this.contextStore.getCheckpoint(tgt.headCheckpointId);
|
|
1942
|
+
const mergedTitle = `[Merge] ${src.name} \u2794 ${tgt.name}`;
|
|
1943
|
+
const mergedDecisions = Array.from(new Set([
|
|
1944
|
+
`\uBE0C\uB79C\uCE58 ${src.name}\uC774 ${tgt.name}\uC73C\uB85C \uBCD1\uD569 \uC644\uB8CC\uB428`,
|
|
1945
|
+
...tgtCp?.keyDecisions || [],
|
|
1946
|
+
...srcCp?.keyDecisions || []
|
|
1947
|
+
]));
|
|
1948
|
+
const mergedCompleted = Array.from(new Set([...tgtCp?.completedSteps || [], ...srcCp?.completedSteps || []]));
|
|
1949
|
+
const mergedPending = Array.from(new Set([...srcCp?.pendingTasks || [], ...tgtCp?.pendingTasks || []]));
|
|
1950
|
+
const mergedFiles = Array.from(new Set([...tgtCp?.activeFiles || [], ...srcCp?.activeFiles || []]));
|
|
1951
|
+
const mergedInvariants = Array.from(new Set([...tgtCp?.activeInvariants || [], ...srcCp?.activeInvariants || []]));
|
|
1952
|
+
const resultingCp = await this.contextStore.saveCheckpoint({
|
|
1953
|
+
sessionId: `branch-${tgt.name}`,
|
|
1954
|
+
title: mergedTitle,
|
|
1955
|
+
goal: tgtCp?.goal || srcCp?.goal || mergedTitle,
|
|
1956
|
+
completedSteps: mergedCompleted,
|
|
1957
|
+
pendingTasks: mergedPending,
|
|
1958
|
+
keyDecisions: mergedDecisions,
|
|
1959
|
+
activeFiles: mergedFiles,
|
|
1960
|
+
activeInvariants: mergedInvariants,
|
|
1961
|
+
artifacts: Array.from(new Set([...tgtCp?.artifacts || [], ...srcCp?.artifacts || []])),
|
|
1962
|
+
autoGit: false,
|
|
1963
|
+
autoResonance: false
|
|
1964
|
+
});
|
|
1965
|
+
const nowIso = new Date().toISOString();
|
|
1966
|
+
const updatedSrc = {
|
|
1967
|
+
...src,
|
|
1968
|
+
status: "merged",
|
|
1969
|
+
mergedInto: tgt.name,
|
|
1970
|
+
updatedAt: nowIso
|
|
1971
|
+
};
|
|
1972
|
+
const updatedTgt = {
|
|
1973
|
+
...tgt,
|
|
1974
|
+
headCheckpointId: resultingCp.id,
|
|
1975
|
+
updatedAt: nowIso
|
|
1976
|
+
};
|
|
1977
|
+
const remaining = this.database.branches.filter((b) => b.name !== src.name && b.name !== tgt.name);
|
|
1978
|
+
this.database = {
|
|
1979
|
+
...this.database,
|
|
1980
|
+
updatedAt: nowIso,
|
|
1981
|
+
currentBranch: tgt.name,
|
|
1982
|
+
branches: [...remaining, updatedTgt, updatedSrc]
|
|
1983
|
+
};
|
|
1984
|
+
this.saveDatabase();
|
|
1985
|
+
return { mergedBranch: updatedSrc, resultingCheckpoint: resultingCp };
|
|
1986
|
+
}
|
|
1987
|
+
abandonBranch(name, failureInfo, safeAlternative) {
|
|
1988
|
+
this.reloadIfNeeded();
|
|
1989
|
+
const target = this.database.branches.find((b) => b.name === name.trim());
|
|
1990
|
+
if (!target)
|
|
1991
|
+
throw new Error(`\uD3D0\uAE30\uD560 \uBE0C\uB79C\uCE58\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${name}`);
|
|
1992
|
+
const infoObj = typeof failureInfo === "string" ? { rootCause: failureInfo, counterMeasure: safeAlternative } : failureInfo || {};
|
|
1993
|
+
const nowIso = new Date().toISOString();
|
|
1994
|
+
const updated = {
|
|
1995
|
+
...target,
|
|
1996
|
+
status: "abandoned",
|
|
1997
|
+
abandonmentReason: infoObj.rootCause || "\uC2E4\uD5D8\uC801 \uAC00\uC124 \uC2E4\uD328\uB85C \uAE30\uAC01\uB428",
|
|
1998
|
+
updatedAt: nowIso
|
|
1999
|
+
};
|
|
2000
|
+
let trapRecorded = false;
|
|
2001
|
+
if (infoObj.rootCause || infoObj.failedApproach) {
|
|
2002
|
+
this.graveyardStore.assertTrap({
|
|
2003
|
+
id: `trap_branch_${target.name}_${Date.now().toString(36)}`,
|
|
2004
|
+
title: `\uBE0C\uB79C\uCE58 ${target.name} \uAE30\uAC01 \uAC00\uC124`,
|
|
2005
|
+
failedApproach: infoObj.failedApproach || target.description || target.name,
|
|
2006
|
+
errorSignature: infoObj.errorSignature,
|
|
2007
|
+
rootCause: infoObj.rootCause || "\uAC00\uC124 \uAC80\uC99D \uC2E4\uD328",
|
|
2008
|
+
counterMeasure: infoObj.counterMeasure || "\uD574\uB2F9 \uBE0C\uB79C\uCE58 \uC811\uADFC\uBC95 \uD3D0\uAE30 \uBC0F \uB300\uC548 \uBE0C\uB79C\uCE58 \uC218\uB9BD \uD544\uC694",
|
|
2009
|
+
triggerKeywords: [
|
|
2010
|
+
target.name,
|
|
2011
|
+
...target.description ? target.description.split(/\s+/) : []
|
|
2012
|
+
]
|
|
2013
|
+
});
|
|
2014
|
+
trapRecorded = true;
|
|
2015
|
+
}
|
|
2016
|
+
const remaining = this.database.branches.filter((b) => b.name !== target.name);
|
|
2017
|
+
this.database = {
|
|
2018
|
+
...this.database,
|
|
2019
|
+
updatedAt: nowIso,
|
|
2020
|
+
currentBranch: this.database.currentBranch === target.name ? "main" : this.database.currentBranch,
|
|
2021
|
+
branches: [...remaining, updated]
|
|
2022
|
+
};
|
|
2023
|
+
this.saveDatabase();
|
|
2024
|
+
return { branch: updated, trapRecorded, success: true };
|
|
2025
|
+
}
|
|
2026
|
+
listBranches() {
|
|
2027
|
+
this.reloadIfNeeded();
|
|
2028
|
+
return [...this.database.branches];
|
|
2029
|
+
}
|
|
2030
|
+
getCurrentBranch() {
|
|
2031
|
+
this.reloadIfNeeded();
|
|
2032
|
+
const curr = this.database.branches.find((b) => b.name === this.database.currentBranch);
|
|
2033
|
+
return curr || {
|
|
2034
|
+
name: "main",
|
|
2035
|
+
baseCheckpointId: "root",
|
|
2036
|
+
headCheckpointId: "root",
|
|
2037
|
+
createdAt: new Date().toISOString(),
|
|
2038
|
+
updatedAt: new Date().toISOString(),
|
|
2039
|
+
status: "active"
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
var init_causal_branch = __esm(() => {
|
|
2044
|
+
init_causal_config();
|
|
2045
|
+
init_causal_context();
|
|
2046
|
+
init_causal_graveyard();
|
|
2047
|
+
});
|
|
2048
|
+
|
|
2049
|
+
// causal-predictor.ts
|
|
2050
|
+
class CausalPredictorEngine {
|
|
2051
|
+
memoryStore;
|
|
2052
|
+
contextStore;
|
|
2053
|
+
constructor(memoryStore, contextStore) {
|
|
2054
|
+
this.memoryStore = memoryStore || new CausalMemoryStore;
|
|
2055
|
+
this.contextStore = contextStore || new ContextCheckpointStore;
|
|
2056
|
+
}
|
|
2057
|
+
analyzeBlastRadius(files, maxHops = 3) {
|
|
2058
|
+
const startTime = performance.now();
|
|
2059
|
+
this.memoryStore.reloadIfNeeded();
|
|
2060
|
+
this.contextStore.reloadIfNeeded();
|
|
2061
|
+
const normalizedFiles = files.map((f) => f.replaceAll("\\", "/").toLowerCase());
|
|
2062
|
+
const resonance = this.memoryStore.queryResonance({
|
|
2063
|
+
files: normalizedFiles,
|
|
2064
|
+
propagateRelations: false
|
|
2065
|
+
});
|
|
2066
|
+
const directRules = resonance.resonanceRules;
|
|
2067
|
+
const rawDb = this.memoryStore.getRawDatabase();
|
|
2068
|
+
const outgoingMap = new Map;
|
|
2069
|
+
const incomingMap = new Map;
|
|
2070
|
+
for (const rel of rawDb.relations) {
|
|
2071
|
+
const fromLower = rel.from.toLowerCase();
|
|
2072
|
+
const toLower = rel.to.toLowerCase();
|
|
2073
|
+
if (!outgoingMap.has(fromLower))
|
|
2074
|
+
outgoingMap.set(fromLower, []);
|
|
2075
|
+
outgoingMap.get(fromLower).push(rel);
|
|
2076
|
+
if (!incomingMap.has(toLower))
|
|
2077
|
+
incomingMap.set(toLower, []);
|
|
2078
|
+
incomingMap.get(toLower).push(rel);
|
|
2079
|
+
}
|
|
2080
|
+
const titleToRule = new Map;
|
|
2081
|
+
for (const rule of rawDb.rules) {
|
|
2082
|
+
titleToRule.set(rule.title.toLowerCase(), rule);
|
|
2083
|
+
titleToRule.set(rule.id.toLowerCase(), rule);
|
|
2084
|
+
}
|
|
2085
|
+
const visited = new Set;
|
|
2086
|
+
const affectedNodes = [];
|
|
2087
|
+
const prefetchedInvariantsSet = new Set;
|
|
2088
|
+
const suggestedFilesSet = new Set;
|
|
2089
|
+
for (const direct of directRules) {
|
|
2090
|
+
if (direct.invariants && Array.isArray(direct.invariants)) {
|
|
2091
|
+
for (const inv of direct.invariants) {
|
|
2092
|
+
prefetchedInvariantsSet.add(inv);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
let currentLevel = directRules.map((r) => r.title.toLowerCase());
|
|
2097
|
+
for (const t of currentLevel)
|
|
2098
|
+
visited.add(t);
|
|
2099
|
+
for (let hop = 1;hop <= maxHops; hop++) {
|
|
2100
|
+
const nextLevel = [];
|
|
2101
|
+
const hopWeight = Math.pow(0.7, hop - 1);
|
|
2102
|
+
for (const currentTitle of currentLevel) {
|
|
2103
|
+
const outRels = outgoingMap.get(currentTitle) || [];
|
|
2104
|
+
const inRels = incomingMap.get(currentTitle) || [];
|
|
2105
|
+
const allRels = [...outRels, ...inRels];
|
|
2106
|
+
for (const rel of allRels) {
|
|
2107
|
+
const neighbor = rel.from.toLowerCase() === currentTitle ? rel.to.toLowerCase() : rel.from.toLowerCase();
|
|
2108
|
+
if (visited.has(neighbor))
|
|
2109
|
+
continue;
|
|
2110
|
+
visited.add(neighbor);
|
|
2111
|
+
nextLevel.push(neighbor);
|
|
2112
|
+
const neighborRule = titleToRule.get(neighbor);
|
|
2113
|
+
if (neighborRule) {
|
|
2114
|
+
let baseScore = 60;
|
|
2115
|
+
const relKind = rel.relation || rel.relationType || "requires";
|
|
2116
|
+
const rType = relKind.toLowerCase();
|
|
2117
|
+
if (rType.includes("trigger") || rType.includes("govern"))
|
|
2118
|
+
baseScore = 90;
|
|
2119
|
+
else if (rType.includes("verify") || rType.includes("produce"))
|
|
2120
|
+
baseScore = 75;
|
|
2121
|
+
const riskScore = Math.round(baseScore * hopWeight);
|
|
2122
|
+
for (const inv of neighborRule.invariants || []) {
|
|
2123
|
+
prefetchedInvariantsSet.add(inv);
|
|
2124
|
+
}
|
|
2125
|
+
if (neighborRule.triggers && Array.isArray(neighborRule.triggers.files)) {
|
|
2126
|
+
for (const f of neighborRule.triggers.files) {
|
|
2127
|
+
suggestedFilesSet.add(f);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
affectedNodes.push({
|
|
2131
|
+
name: neighborRule.title || neighborRule.name || neighborRule.id,
|
|
2132
|
+
domain: neighborRule.domain,
|
|
2133
|
+
hopLevel: hop,
|
|
2134
|
+
relationType: relKind,
|
|
2135
|
+
riskReason: `${hop}\uB2E8\uACC4 \uC778\uACFC \uC5F0\uACB0 (\`${rel.from}\` \u2500\u2500[${relKind}]\u2500\u2500\u25B6 \`${rel.to}\`)`,
|
|
2136
|
+
riskScore,
|
|
2137
|
+
requiredInvariants: neighborRule.invariants || []
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
if (nextLevel.length === 0)
|
|
2143
|
+
break;
|
|
2144
|
+
currentLevel = nextLevel;
|
|
2145
|
+
}
|
|
2146
|
+
const recentCheckpoints = this.contextStore.listCheckpoints({ limit: 15 });
|
|
2147
|
+
for (const cp of recentCheckpoints) {
|
|
2148
|
+
const hasInputFile = cp.modifiedFiles.some((mf) => normalizedFiles.some((nf) => mf.toLowerCase().includes(nf) || nf.includes(mf.toLowerCase())));
|
|
2149
|
+
if (hasInputFile) {
|
|
2150
|
+
for (const f of cp.modifiedFiles) {
|
|
2151
|
+
if (!normalizedFiles.includes(f.toLowerCase())) {
|
|
2152
|
+
suggestedFilesSet.add(f);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
affectedNodes.sort((a, b) => b.riskScore - a.riskScore);
|
|
2158
|
+
const maxRiskScore = affectedNodes.length > 0 ? affectedNodes[0].riskScore : 0;
|
|
2159
|
+
let overallRisk = "LOW";
|
|
2160
|
+
if (maxRiskScore >= 80)
|
|
2161
|
+
overallRisk = "CRITICAL";
|
|
2162
|
+
else if (maxRiskScore >= 60)
|
|
2163
|
+
overallRisk = "HIGH";
|
|
2164
|
+
else if (maxRiskScore >= 40)
|
|
2165
|
+
overallRisk = "MEDIUM";
|
|
2166
|
+
const durationMs = Number((performance.now() - startTime).toFixed(2));
|
|
2167
|
+
return {
|
|
2168
|
+
triggerFiles: files,
|
|
2169
|
+
overallRisk,
|
|
2170
|
+
maxRiskScore,
|
|
2171
|
+
affectedNodes,
|
|
2172
|
+
prefetchedInvariants: Array.from(prefetchedInvariantsSet),
|
|
2173
|
+
suggestedTargetFiles: Array.from(suggestedFilesSet).slice(0, 10),
|
|
2174
|
+
durationMs
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
static formatPredictReport(report) {
|
|
2178
|
+
const lines = [
|
|
2179
|
+
`### \uD83D\uDD2E [\uC778\uACFC \uC5F0\uC1C4 \uD30C\uAE09 \uC608\uCE21 \uBD84\uC11D] \uC704\uD5D8\uB3C4: **${report.overallRisk}** (\uCD5C\uACE0 \uC704\uD5D8 \uC810\uC218: ${report.maxRiskScore}/100, \uC18C\uC694: ${report.durationMs}ms)`,
|
|
2180
|
+
`- **\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C**: ${report.triggerFiles.map((f) => `\`${f}\``).join(", ")}`
|
|
2181
|
+
];
|
|
2182
|
+
if (report.affectedNodes.length > 0) {
|
|
2183
|
+
lines.push(`
|
|
2184
|
+
#### \uD83D\uDCA5 \uC5F0\uC1C4 \uD30C\uAE09 \uC601\uD5A5 \uB178\uB4DC (Blast Radius Nodes)`);
|
|
2185
|
+
for (const node of report.affectedNodes) {
|
|
2186
|
+
lines.push(` - **[${node.riskScore}\uC810 | ${node.hopLevel}\uCC28]** \`${node.name}\` (${node.domain}) \u2794 ${node.riskReason}`);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (report.suggestedTargetFiles.length > 0) {
|
|
2190
|
+
lines.push(`
|
|
2191
|
+
#### \uD83C\uDFAF \uD568\uAED8 \uAC80\uC99D/\uC218\uC815\uD574\uC57C \uD560 \uCD94\uCC9C \uD0C0\uAE43 \uD30C\uC77C (Prefetched Targets)`);
|
|
2192
|
+
for (const tf of report.suggestedTargetFiles) {
|
|
2193
|
+
lines.push(` - \`${tf}\``);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
if (report.prefetchedInvariants.length > 0) {
|
|
2197
|
+
lines.push(`
|
|
2198
|
+
#### \uD83D\uDEE1\uFE0F \uC0AC\uC804 \uB85C\uB4DC\uB41C \uD30C\uAE09 \uBD88\uBCC0 \uADDC\uCE59 (Prefetched Invariants)`);
|
|
2199
|
+
for (const inv of report.prefetchedInvariants.slice(0, 8)) {
|
|
2200
|
+
lines.push(` - ${inv}`);
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
return lines.join(`
|
|
2204
|
+
`);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
var init_causal_predictor = __esm(() => {
|
|
2208
|
+
init_causal_store();
|
|
2209
|
+
init_causal_context();
|
|
2210
|
+
});
|
|
2211
|
+
|
|
2212
|
+
// causal-synthesizer.ts
|
|
2213
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
2214
|
+
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
2215
|
+
|
|
2216
|
+
class CausalSynthesizerEngine {
|
|
2217
|
+
contextStore;
|
|
2218
|
+
memoryStore;
|
|
2219
|
+
constructor(contextStore, memoryStore) {
|
|
2220
|
+
this.contextStore = contextStore || new ContextCheckpointStore;
|
|
2221
|
+
this.memoryStore = memoryStore || new CausalMemoryStore;
|
|
2222
|
+
}
|
|
2223
|
+
async inspectGitChanges(cwd = process.cwd()) {
|
|
2224
|
+
try {
|
|
2225
|
+
const gitHead = await detectGitHeadInfo(cwd);
|
|
2226
|
+
const statusProc = spawn3(["git", "status", "--porcelain"], {
|
|
2227
|
+
cwd,
|
|
2228
|
+
stdout: "pipe",
|
|
2229
|
+
stderr: "pipe"
|
|
2230
|
+
});
|
|
2231
|
+
const statusOutput = (await new Response(statusProc.stdout).text()).trim();
|
|
2232
|
+
await statusProc.exited;
|
|
2233
|
+
const modifiedFiles = [];
|
|
2234
|
+
const addedFiles = [];
|
|
2235
|
+
const deletedFiles = [];
|
|
2236
|
+
const untrackedFiles = [];
|
|
2237
|
+
if (statusOutput.length > 0) {
|
|
2238
|
+
for (const line of statusOutput.split(`
|
|
2239
|
+
`)) {
|
|
2240
|
+
const trimmed = line.trimEnd();
|
|
2241
|
+
if (trimmed.length < 3)
|
|
2242
|
+
continue;
|
|
2243
|
+
const statusCode = trimmed.slice(0, 2);
|
|
2244
|
+
const rawFilePath = trimmed.slice(3).trim().replace(/^"|"$/g, "");
|
|
2245
|
+
const normalizedPath = rawFilePath.replaceAll("\\", "/");
|
|
2246
|
+
if (statusCode.includes("M")) {
|
|
2247
|
+
modifiedFiles.push(normalizedPath);
|
|
2248
|
+
} else if (statusCode.includes("A")) {
|
|
2249
|
+
addedFiles.push(normalizedPath);
|
|
2250
|
+
} else if (statusCode.includes("D")) {
|
|
2251
|
+
deletedFiles.push(normalizedPath);
|
|
2252
|
+
} else if (statusCode.includes("?")) {
|
|
2253
|
+
untrackedFiles.push(normalizedPath);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
const stats = await this.parseGitStats(cwd);
|
|
2258
|
+
const extractedSymbols = await this.extractSymbolsFromDiff(cwd);
|
|
2259
|
+
const allActiveFiles = Array.from(new Set([...modifiedFiles, ...addedFiles, ...untrackedFiles]));
|
|
2260
|
+
const detectedDomains = this.detectDomainsFromFiles(allActiveFiles);
|
|
2261
|
+
return {
|
|
2262
|
+
cwd,
|
|
2263
|
+
branch: gitHead.branch,
|
|
2264
|
+
commit: gitHead.commit,
|
|
2265
|
+
modifiedFiles,
|
|
2266
|
+
addedFiles,
|
|
2267
|
+
deletedFiles,
|
|
2268
|
+
untrackedFiles,
|
|
2269
|
+
allActiveFiles,
|
|
2270
|
+
stats,
|
|
2271
|
+
extractedSymbols,
|
|
2272
|
+
detectedDomains
|
|
2273
|
+
};
|
|
2274
|
+
} catch {
|
|
2275
|
+
return this.fallbackFilesystemScan(cwd);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
async parseGitStats(cwd) {
|
|
2279
|
+
try {
|
|
2280
|
+
const proc = spawn3(["git", "diff", "HEAD", "--shortstat"], {
|
|
2281
|
+
cwd,
|
|
2282
|
+
stdout: "pipe",
|
|
2283
|
+
stderr: "pipe"
|
|
2284
|
+
});
|
|
2285
|
+
const out = (await new Response(proc.stdout).text()).trim();
|
|
2286
|
+
await proc.exited;
|
|
2287
|
+
let filesChanged = 0;
|
|
2288
|
+
let insertions = 0;
|
|
2289
|
+
let deletions = 0;
|
|
2290
|
+
const filesMatch = out.match(/(\d+)\s+files?\s+changed/i);
|
|
2291
|
+
const insertMatch = out.match(/(\d+)\s+insertions?\(\+\)/i);
|
|
2292
|
+
const deleteMatch = out.match(/(\d+)\s+deletions?\(-\)/i);
|
|
2293
|
+
if (filesMatch)
|
|
2294
|
+
filesChanged = parseInt(filesMatch[1], 10);
|
|
2295
|
+
if (insertMatch)
|
|
2296
|
+
insertions = parseInt(insertMatch[1], 10);
|
|
2297
|
+
if (deleteMatch)
|
|
2298
|
+
deletions = parseInt(deleteMatch[1], 10);
|
|
2299
|
+
return { filesChanged, insertions, deletions };
|
|
2300
|
+
} catch {
|
|
2301
|
+
return { filesChanged: 0, insertions: 0, deletions: 0 };
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
async extractSymbolsFromDiff(cwd) {
|
|
2305
|
+
const functions = new Set;
|
|
2306
|
+
const classes = new Set;
|
|
2307
|
+
const interfaces = new Set;
|
|
2308
|
+
const types = new Set;
|
|
2309
|
+
try {
|
|
2310
|
+
const proc = spawn3(["git", "diff", "HEAD", "-U1"], {
|
|
2311
|
+
cwd,
|
|
2312
|
+
stdout: "pipe",
|
|
2313
|
+
stderr: "pipe"
|
|
2314
|
+
});
|
|
2315
|
+
const diffText = await new Response(proc.stdout).text();
|
|
2316
|
+
await proc.exited;
|
|
2317
|
+
if (!diffText || diffText.length === 0) {
|
|
2318
|
+
return { functions: [], classes: [], interfaces: [], types: [] };
|
|
2319
|
+
}
|
|
2320
|
+
for (const line of diffText.split(`
|
|
2321
|
+
`)) {
|
|
2322
|
+
if (!line.startsWith("+") && !line.startsWith("@@"))
|
|
2323
|
+
continue;
|
|
2324
|
+
const content = line.startsWith("+") ? line.slice(1).trim() : line;
|
|
2325
|
+
const fnMatch = content.match(/(?:export\s+)?(?:async\s+)?function\s+([A-Za-z0-9_$]+)/) || content.match(/(?:export\s+)?(?:const|let)\s+([A-Za-z0-9_$]+)\s*=\s*(?:async\s*)?\(/);
|
|
2326
|
+
if (fnMatch && fnMatch[1]) {
|
|
2327
|
+
functions.add(fnMatch[1]);
|
|
2328
|
+
}
|
|
2329
|
+
const clsMatch = content.match(/(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z0-9_$]+)/);
|
|
2330
|
+
if (clsMatch && clsMatch[1]) {
|
|
2331
|
+
classes.add(clsMatch[1]);
|
|
2332
|
+
}
|
|
2333
|
+
const ifaceMatch = content.match(/(?:export\s+)?interface\s+([A-Za-z0-9_$]+)/);
|
|
2334
|
+
if (ifaceMatch && ifaceMatch[1]) {
|
|
2335
|
+
interfaces.add(ifaceMatch[1]);
|
|
2336
|
+
}
|
|
2337
|
+
const typeMatch = content.match(/(?:export\s+)?type\s+([A-Za-z0-9_$]+)\s*=/);
|
|
2338
|
+
if (typeMatch && typeMatch[1]) {
|
|
2339
|
+
types.add(typeMatch[1]);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return {
|
|
2343
|
+
functions: Array.from(functions).slice(0, 15),
|
|
2344
|
+
classes: Array.from(classes).slice(0, 10),
|
|
2345
|
+
interfaces: Array.from(interfaces).slice(0, 10),
|
|
2346
|
+
types: Array.from(types).slice(0, 10)
|
|
2347
|
+
};
|
|
2348
|
+
} catch {
|
|
2349
|
+
return { functions: [], classes: [], interfaces: [], types: [] };
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
detectDomainsFromFiles(files) {
|
|
2353
|
+
const domains = new Set;
|
|
2354
|
+
for (const file of files) {
|
|
2355
|
+
const lower = file.toLowerCase();
|
|
2356
|
+
if (lower.includes("test") || lower.endsWith(".test.ts") || lower.endsWith(".spec.ts")) {
|
|
2357
|
+
domains.add("testing");
|
|
2358
|
+
}
|
|
2359
|
+
if (lower.includes("compiler") || lower.includes("ast")) {
|
|
2360
|
+
domains.add("compiler");
|
|
2361
|
+
}
|
|
2362
|
+
if (lower.includes("memory") || lower.includes("causal")) {
|
|
2363
|
+
domains.add("memory");
|
|
2364
|
+
}
|
|
2365
|
+
if (lower.includes("db") || lower.includes("database") || lower.includes("sqlite")) {
|
|
2366
|
+
domains.add("database");
|
|
2367
|
+
}
|
|
2368
|
+
if (lower.includes("mesh") || lower.includes("distributed") || lower.includes("peer")) {
|
|
2369
|
+
domains.add("mesh");
|
|
2370
|
+
}
|
|
2371
|
+
if (lower.includes("runner") || lower.includes("execution")) {
|
|
2372
|
+
domains.add("runner");
|
|
2373
|
+
}
|
|
2374
|
+
if (lower.includes("bus") || lower.includes("event")) {
|
|
2375
|
+
domains.add("bus");
|
|
2376
|
+
}
|
|
2377
|
+
if (lower.includes("guard") || lower.includes("security")) {
|
|
2378
|
+
domains.add("security");
|
|
2379
|
+
}
|
|
2380
|
+
if (lower.includes("studio") || lower.includes("ui") || lower.includes("view")) {
|
|
2381
|
+
domains.add("frontend");
|
|
2382
|
+
}
|
|
2383
|
+
if (lower.includes("lens") || lower.includes("obsidian")) {
|
|
2384
|
+
domains.add("obsidian-lens");
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
if (domains.size === 0) {
|
|
2388
|
+
domains.add("core");
|
|
2389
|
+
}
|
|
2390
|
+
return Array.from(domains);
|
|
2391
|
+
}
|
|
2392
|
+
fallbackFilesystemScan(cwd) {
|
|
2393
|
+
try {
|
|
2394
|
+
const modified = [];
|
|
2395
|
+
if (existsSync5(cwd)) {
|
|
2396
|
+
const entries = readdirSync(cwd, { withFileTypes: true });
|
|
2397
|
+
for (const entry of entries) {
|
|
2398
|
+
if (entry.isFile() && !entry.name.startsWith(".")) {
|
|
2399
|
+
modified.push(entry.name);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
cwd,
|
|
2405
|
+
modifiedFiles: modified,
|
|
2406
|
+
addedFiles: [],
|
|
2407
|
+
deletedFiles: [],
|
|
2408
|
+
untrackedFiles: [],
|
|
2409
|
+
allActiveFiles: modified,
|
|
2410
|
+
stats: { insertions: 0, deletions: 0, filesChanged: modified.length },
|
|
2411
|
+
extractedSymbols: { functions: [], classes: [], interfaces: [], types: [] },
|
|
2412
|
+
detectedDomains: ["general"]
|
|
2413
|
+
};
|
|
2414
|
+
} catch {
|
|
2415
|
+
return {
|
|
2416
|
+
cwd,
|
|
2417
|
+
modifiedFiles: [],
|
|
2418
|
+
addedFiles: [],
|
|
2419
|
+
deletedFiles: [],
|
|
2420
|
+
untrackedFiles: [],
|
|
2421
|
+
allActiveFiles: [],
|
|
2422
|
+
stats: { insertions: 0, deletions: 0, filesChanged: 0 },
|
|
2423
|
+
extractedSymbols: { functions: [], classes: [], interfaces: [], types: [] },
|
|
2424
|
+
detectedDomains: ["unknown"]
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
synthesizeIntent(summary, customTitle) {
|
|
2429
|
+
const completedSteps = [];
|
|
2430
|
+
const pendingTasks = [];
|
|
2431
|
+
const keyDecisions = [];
|
|
2432
|
+
const activeInvariants = [];
|
|
2433
|
+
const suggestedKeywords = [...summary.detectedDomains];
|
|
2434
|
+
if (summary.extractedSymbols.classes.length > 0) {
|
|
2435
|
+
completedSteps.push(`\uD575\uC2EC \uD074\uB798\uC2A4 \uBAA8\uB4C8\uD654 \uBC0F \uC120\uC5B8: ${summary.extractedSymbols.classes.join(", ")}`);
|
|
2436
|
+
}
|
|
2437
|
+
if (summary.extractedSymbols.interfaces.length > 0) {
|
|
2438
|
+
completedSteps.push(`\uD0C0\uC785 \uACC4\uC57D \uBC0F \uC778\uD130\uD398\uC774\uC2A4 \uC815\uC758: ${summary.extractedSymbols.interfaces.join(", ")}`);
|
|
2439
|
+
}
|
|
2440
|
+
if (summary.extractedSymbols.functions.length > 0) {
|
|
2441
|
+
completedSteps.push(`\uC8FC\uC694 \uD568\uC218 \uBC0F \uC54C\uACE0\uB9AC\uC998 \uAD6C\uD604: ${summary.extractedSymbols.functions.slice(0, 5).join(", ")}`);
|
|
2442
|
+
}
|
|
2443
|
+
if (summary.detectedDomains.includes("testing")) {
|
|
2444
|
+
completedSteps.push("\uB2E8\uC704 \uBC0F \uD1B5\uD569 \uAC80\uC99D \uD14C\uC2A4\uD2B8 \uC2A4\uC704\uD2B8 \uC791\uC131 \uBC0F \uC815\uBC00 \uAC80\uC99D \uC900\uBE44");
|
|
2445
|
+
}
|
|
2446
|
+
if (summary.detectedDomains.includes("memory")) {
|
|
2447
|
+
completedSteps.push("\uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC \uBC0F \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uC544\uD0A4\uD14D\uCC98 \uBC18\uC601");
|
|
2448
|
+
activeInvariants.push("\uBA54\uBAA8\uB9AC \uCFFC\uB9AC \uC2DC 0ms \uC989\uAC01 \uC751\uB2F5 \uBC0F \uBB34\uC190\uC2E4 \uC778\uACFC\uC728 \uBCF4\uC874");
|
|
2449
|
+
}
|
|
2450
|
+
if (summary.detectedDomains.includes("obsidian-lens")) {
|
|
2451
|
+
completedSteps.push("\uC635\uC2DC\uB514\uC5B8 \uB9C8\uD06C\uB2E4\uC6B4 \uBCFC\uD2B8 \uBC0F \uC2DC\uAC01\uC801 \uCE94\uBC84\uC2A4 \uC5F0\uB3D9 \uACC4\uCE35 \uAD6C\uCD95");
|
|
2452
|
+
}
|
|
2453
|
+
if (summary.addedFiles.length > 0) {
|
|
2454
|
+
completedSteps.push(`\uC2E0\uADDC \uC544\uD0A4\uD14D\uCC98 \uD30C\uC77C \uC0DD\uC131 (${summary.addedFiles.length}\uAC1C \uD30C\uC77C: ${summary.addedFiles.slice(0, 3).join(", ")}${summary.addedFiles.length > 3 ? " \uC678" : ""})`);
|
|
2455
|
+
}
|
|
2456
|
+
if (summary.modifiedFiles.length > 0) {
|
|
2457
|
+
completedSteps.push(`\uAE30\uC874 \uBAA8\uB4C8 \uB9AC\uD329\uD130\uB9C1 \uBC0F \uAE30\uB2A5 \uD655\uC7A5 (${summary.modifiedFiles.length}\uAC1C \uD30C\uC77C)`);
|
|
2458
|
+
}
|
|
2459
|
+
if (completedSteps.length === 0) {
|
|
2460
|
+
completedSteps.push("\uC18C\uC2A4 \uCF54\uB4DC \uBCC0\uACBD \uBC0F \uBAA8\uB4C8 \uAD6C\uC870 \uC815\uBE44");
|
|
2461
|
+
}
|
|
2462
|
+
const hasTests = summary.allActiveFiles.some((f) => f.includes("test") || f.endsWith(".test.ts"));
|
|
2463
|
+
if (!hasTests) {
|
|
2464
|
+
pendingTasks.push("\uCD94\uAC00/\uC218\uC815\uB41C \uBAA8\uB4C8\uC5D0 \uB300\uD55C \uD3EC\uAD04\uC801 \uD1B5\uD569 \uD14C\uC2A4\uD2B8 \uC2A4\uC704\uD2B8 \uC791\uC131 \uBC0F \uD68C\uADC0 \uBC29\uC9C0");
|
|
2465
|
+
}
|
|
2466
|
+
pendingTasks.push("Bun \uB7F0\uD0C0\uC784 \uAE30\uBC18 \uBE4C\uB4DC \uAC80\uC99D (`bun run build`) \uBC0F \uBB34\uC624\uB958 \uD655\uC778");
|
|
2467
|
+
pendingTasks.push("\uC804\uC5ED \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uB370\uC774\uD130\uBCA0\uC774\uC2A4 \uB3D9\uAE30\uD654 \uBC0F \uAE30\uC5B5 \uC601\uC18D\uD654");
|
|
2468
|
+
keyDecisions.push("\uD558\uB4DC\uCF54\uB529 \uBC30\uC81C \uBC0F \uD658\uACBD \uBCC0\uC218/\uC124\uC815 \uBAA8\uB4C8 \uC911\uC2EC\uC758 \uC124\uC815 \uC544\uD0A4\uD14D\uCC98 \uC801\uC6A9");
|
|
2469
|
+
keyDecisions.push("SOLID \uB2E8\uC77C \uCC45\uC784 \uC6D0\uCE59(SRP)\uC744 \uC900\uC218\uD55C \uB3C5\uB9BD \uC5D4\uC9C4 \uBD84\uB9AC");
|
|
2470
|
+
if (summary.detectedDomains.includes("testing")) {
|
|
2471
|
+
keyDecisions.push("\uBAA8\uC758(Mock)\uB97C \uC9C0\uC591\uD558\uACE0 \uC2E4\uC81C \uACA9\uB9AC \uD658\uACBD\uC5D0\uC11C\uC758 End-to-End \uB3D9\uC791 \uAC80\uC99D \uC218\uD589");
|
|
2472
|
+
}
|
|
2473
|
+
const primaryDomain = summary.detectedDomains[0] || "core";
|
|
2474
|
+
const autoTitle = customTitle || `[\uC790\uB3D9 \uC5ED\uD569\uC131] ${primaryDomain.toUpperCase()} \uBAA8\uB4C8 \uC544\uD0A4\uD14D\uCC98 \uBCC0\uACBD (${summary.stats.filesChanged || summary.allActiveFiles.length}\uAC1C \uD30C\uC77C \uBC18\uC601)`;
|
|
2475
|
+
const autoGoal = `${summary.detectedDomains.join(", ")} \uC601\uC5ED\uC758 \uAE30\uB2A5\uC744 \uD655\uC7A5\uD558\uACE0 \uBCC0\uACBD \uC0AC\uD56D\uC744 \uBB34\uACB0\uD558\uAC8C \uC815\uD569\uD558\uB294 \uC791\uC5C5`;
|
|
2476
|
+
return {
|
|
2477
|
+
title: autoTitle,
|
|
2478
|
+
goal: autoGoal,
|
|
2479
|
+
completedSteps,
|
|
2480
|
+
pendingTasks,
|
|
2481
|
+
keyDecisions,
|
|
2482
|
+
activeInvariants,
|
|
2483
|
+
suggestedKeywords
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
async autoCheckpoint(cwd = process.cwd(), options = {}) {
|
|
2487
|
+
const summary = await this.inspectGitChanges(cwd);
|
|
2488
|
+
const synthesized = this.synthesizeIntent(summary, options.title);
|
|
2489
|
+
const mergedCompletedSteps = [
|
|
2490
|
+
...synthesized.completedSteps,
|
|
2491
|
+
...options.extraCompletedSteps || []
|
|
2492
|
+
];
|
|
2493
|
+
const mergedPendingTasks = [
|
|
2494
|
+
...synthesized.pendingTasks,
|
|
2495
|
+
...options.extraPendingTasks || []
|
|
2496
|
+
];
|
|
2497
|
+
const mergedKeyDecisions = [
|
|
2498
|
+
...synthesized.keyDecisions,
|
|
2499
|
+
...options.extraKeyDecisions || []
|
|
2500
|
+
];
|
|
2501
|
+
const mergedInvariants = [
|
|
2502
|
+
...synthesized.activeInvariants,
|
|
2503
|
+
...options.extraInvariants || []
|
|
2504
|
+
];
|
|
2505
|
+
const saveInput = {
|
|
2506
|
+
title: options.title || synthesized.title,
|
|
2507
|
+
goal: options.goal || synthesized.goal,
|
|
2508
|
+
cwd,
|
|
2509
|
+
activeFiles: summary.allActiveFiles,
|
|
2510
|
+
modifiedFiles: [...summary.modifiedFiles, ...summary.addedFiles],
|
|
2511
|
+
completedSteps: mergedCompletedSteps,
|
|
2512
|
+
pendingTasks: mergedPendingTasks,
|
|
2513
|
+
keyDecisions: mergedKeyDecisions,
|
|
2514
|
+
activeInvariants: mergedInvariants,
|
|
2515
|
+
keywords: synthesized.suggestedKeywords,
|
|
2516
|
+
domain: summary.detectedDomains[0] || "core",
|
|
2517
|
+
autoGit: true,
|
|
2518
|
+
autoResonance: options.autoResonance ?? true,
|
|
2519
|
+
customMeta: {
|
|
2520
|
+
stats: summary.stats,
|
|
2521
|
+
symbols: summary.extractedSymbols,
|
|
2522
|
+
synthesizedAt: new Date().toISOString(),
|
|
2523
|
+
...options.customMeta
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
return this.contextStore.saveCheckpoint(saveInput);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
var init_causal_synthesizer = __esm(() => {
|
|
2530
|
+
init_causal_context();
|
|
2531
|
+
init_causal_store();
|
|
2532
|
+
});
|
|
2533
|
+
|
|
2534
|
+
// causal-lens.ts
|
|
2535
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync as renameSync4 } from "fs";
|
|
2536
|
+
import { resolve as resolve4, basename } from "path";
|
|
2537
|
+
|
|
2538
|
+
class CausalObsidianLensEngine {
|
|
2539
|
+
memoryStore;
|
|
2540
|
+
contextStore;
|
|
2541
|
+
graveyardStore;
|
|
2542
|
+
timeSeriesEngine;
|
|
2543
|
+
constructor(memoryStore, contextStore, graveyardStore, timeSeriesEngine) {
|
|
2544
|
+
this.memoryStore = memoryStore || new CausalMemoryStore;
|
|
2545
|
+
this.contextStore = contextStore || new ContextCheckpointStore;
|
|
2546
|
+
this.graveyardStore = graveyardStore || new CausalGraveyardStore;
|
|
2547
|
+
this.timeSeriesEngine = timeSeriesEngine || new CausalTimeSeriesEngine(this.contextStore, this.memoryStore);
|
|
2548
|
+
}
|
|
2549
|
+
async exportToVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
|
|
2550
|
+
const startTime = performance.now();
|
|
2551
|
+
try {
|
|
2552
|
+
this.memoryStore.reloadIfNeeded();
|
|
2553
|
+
this.contextStore.reloadIfNeeded();
|
|
2554
|
+
this.graveyardStore.reloadIfNeeded();
|
|
2555
|
+
const mentalRoot = resolve4(vaultRoot, CAUSAL_MEMORY_CONFIG.OBSIDIAN_LENS_SUBDIR);
|
|
2556
|
+
const rulesDir = resolve4(mentalRoot, "Rules");
|
|
2557
|
+
const graveyardDir = resolve4(mentalRoot, "Graveyard");
|
|
2558
|
+
const checkpointsDir = resolve4(mentalRoot, "Checkpoints");
|
|
2559
|
+
mkdirSync5(rulesDir, { recursive: true });
|
|
2560
|
+
mkdirSync5(graveyardDir, { recursive: true });
|
|
2561
|
+
mkdirSync5(checkpointsDir, { recursive: true });
|
|
2562
|
+
const rawDb = this.memoryStore.getRawDatabase();
|
|
2563
|
+
let exportedRules = 0;
|
|
2564
|
+
for (const rule of rawDb.rules) {
|
|
2565
|
+
const ruleTitle = rule.title || rule.name || rule.id;
|
|
2566
|
+
const safeFileName = this.sanitizeFileName(ruleTitle);
|
|
2567
|
+
const filePath = resolve4(rulesDir, `${safeFileName}.md`);
|
|
2568
|
+
const content = this.renderRuleToMarkdown(rule);
|
|
2569
|
+
this.atomicWriteFile(filePath, content);
|
|
2570
|
+
exportedRules++;
|
|
2571
|
+
}
|
|
2572
|
+
const allTraps = this.graveyardStore.listTraps();
|
|
2573
|
+
let exportedGraveyard = 0;
|
|
2574
|
+
for (const trap of allTraps) {
|
|
2575
|
+
const trapTitle = trap.title || trap.failedApproach || trap.id;
|
|
2576
|
+
const safeFileName = this.sanitizeFileName(trapTitle);
|
|
2577
|
+
const filePath = resolve4(graveyardDir, `${safeFileName}.md`);
|
|
2578
|
+
const content = this.renderTrapToMarkdown(trap);
|
|
2579
|
+
this.atomicWriteFile(filePath, content);
|
|
2580
|
+
exportedGraveyard++;
|
|
2581
|
+
}
|
|
2582
|
+
const checkpoints = this.contextStore.listCheckpoints({ limit: 100 });
|
|
2583
|
+
let exportedCheckpoints = 0;
|
|
2584
|
+
for (const cp of checkpoints) {
|
|
2585
|
+
const filePath = resolve4(checkpointsDir, `${cp.id}.md`);
|
|
2586
|
+
const content = this.renderCheckpointToMarkdown(cp);
|
|
2587
|
+
this.atomicWriteFile(filePath, content);
|
|
2588
|
+
exportedCheckpoints++;
|
|
2589
|
+
}
|
|
2590
|
+
const timelinePath = resolve4(mentalRoot, "Timeline.md");
|
|
2591
|
+
const timelineContent = this.renderTimelineMarkdown();
|
|
2592
|
+
this.atomicWriteFile(timelinePath, timelineContent);
|
|
2593
|
+
const canvasPath = resolve4(mentalRoot, "Causal-Architecture.canvas");
|
|
2594
|
+
const canvasDoc = this.generateCanvasDocument(rawDb.rules, rawDb.relations);
|
|
2595
|
+
this.atomicWriteFile(canvasPath, JSON.stringify(canvasDoc, null, 2));
|
|
2596
|
+
const durationMs = Number((performance.now() - startTime).toFixed(2));
|
|
2597
|
+
return {
|
|
2598
|
+
success: true,
|
|
2599
|
+
vaultPath: mentalRoot,
|
|
2600
|
+
exportedRules,
|
|
2601
|
+
exportedGraveyard,
|
|
2602
|
+
exportedCheckpoints,
|
|
2603
|
+
canvasGenerated: true,
|
|
2604
|
+
timelineGenerated: true,
|
|
2605
|
+
durationMs
|
|
2606
|
+
};
|
|
2607
|
+
} catch (err) {
|
|
2608
|
+
const durationMs = Number((performance.now() - startTime).toFixed(2));
|
|
2609
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2610
|
+
return {
|
|
2611
|
+
success: false,
|
|
2612
|
+
vaultPath: vaultRoot,
|
|
2613
|
+
exportedRules: 0,
|
|
2614
|
+
exportedGraveyard: 0,
|
|
2615
|
+
exportedCheckpoints: 0,
|
|
2616
|
+
canvasGenerated: false,
|
|
2617
|
+
timelineGenerated: false,
|
|
2618
|
+
durationMs,
|
|
2619
|
+
error: `\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uB0B4\uBCF4\uB0B4\uAE30 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${message}`
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
async importFromVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
|
|
2624
|
+
const startTime = performance.now();
|
|
2625
|
+
try {
|
|
2626
|
+
const mentalRoot = resolve4(vaultRoot, CAUSAL_MEMORY_CONFIG.OBSIDIAN_LENS_SUBDIR);
|
|
2627
|
+
const rulesDir = resolve4(mentalRoot, "Rules");
|
|
2628
|
+
const graveyardDir = resolve4(mentalRoot, "Graveyard");
|
|
2629
|
+
let importedRules = 0;
|
|
2630
|
+
let importedGraveyard = 0;
|
|
2631
|
+
if (existsSync6(rulesDir)) {
|
|
2632
|
+
const ruleFiles = readdirSync2(rulesDir).filter((f) => f.endsWith(".md"));
|
|
2633
|
+
for (const file of ruleFiles) {
|
|
2634
|
+
const filePath = resolve4(rulesDir, file);
|
|
2635
|
+
const rawText = readFileSync5(filePath, "utf-8");
|
|
2636
|
+
const parsed = this.parseRuleMarkdown(rawText, basename(file, ".md"));
|
|
2637
|
+
if (parsed) {
|
|
2638
|
+
this.memoryStore.assertRule(parsed);
|
|
2639
|
+
importedRules++;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
if (existsSync6(graveyardDir)) {
|
|
2644
|
+
const trapFiles = readdirSync2(graveyardDir).filter((f) => f.endsWith(".md"));
|
|
2645
|
+
for (const file of trapFiles) {
|
|
2646
|
+
const filePath = resolve4(graveyardDir, file);
|
|
2647
|
+
const rawText = readFileSync5(filePath, "utf-8");
|
|
2648
|
+
const parsed = this.parseTrapMarkdown(rawText, basename(file, ".md"));
|
|
2649
|
+
if (parsed) {
|
|
2650
|
+
this.graveyardStore.assertTrap(parsed);
|
|
2651
|
+
importedGraveyard++;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
const durationMs = Number((performance.now() - startTime).toFixed(2));
|
|
2656
|
+
return {
|
|
2657
|
+
success: true,
|
|
2658
|
+
vaultPath: mentalRoot,
|
|
2659
|
+
importedRules,
|
|
2660
|
+
importedGraveyard,
|
|
2661
|
+
durationMs
|
|
2662
|
+
};
|
|
2663
|
+
} catch (err) {
|
|
2664
|
+
const durationMs = Number((performance.now() - startTime).toFixed(2));
|
|
2665
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2666
|
+
return {
|
|
2667
|
+
success: false,
|
|
2668
|
+
vaultPath: vaultRoot,
|
|
2669
|
+
importedRules: 0,
|
|
2670
|
+
importedGraveyard: 0,
|
|
2671
|
+
durationMs,
|
|
2672
|
+
error: `\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uAC00\uC838\uC624\uAE30 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${message}`
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
async syncWithVault(vaultRoot = CAUSAL_MEMORY_CONFIG.DEFAULT_OBSIDIAN_VAULT_PATH) {
|
|
2677
|
+
const importResult = await this.importFromVault(vaultRoot);
|
|
2678
|
+
const exportResult = await this.exportToVault(vaultRoot);
|
|
2679
|
+
return {
|
|
2680
|
+
success: importResult.success && exportResult.success,
|
|
2681
|
+
exportResult,
|
|
2682
|
+
importResult,
|
|
2683
|
+
syncedAt: new Date().toISOString()
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
renderRuleToMarkdown(rule) {
|
|
2687
|
+
const title = rule.title || rule.name || rule.id;
|
|
2688
|
+
const lines = [
|
|
2689
|
+
"---",
|
|
2690
|
+
`id: "${rule.id}"`,
|
|
2691
|
+
`name: "${title.replaceAll('"', "\\\"")}"`,
|
|
2692
|
+
`domain: "${rule.domain}"`,
|
|
2693
|
+
`confidence: ${rule.confidence ?? 1}`,
|
|
2694
|
+
`weight: ${rule.weight ?? 1}`,
|
|
2695
|
+
`updatedAt: "${rule.updatedAt || new Date().toISOString()}"`,
|
|
2696
|
+
"invariants:",
|
|
2697
|
+
...(rule.invariants || []).map((inv) => ` - "${inv.replaceAll('"', "\\\"")}"`),
|
|
2698
|
+
"tags:",
|
|
2699
|
+
' - "synapse/causal-rule"',
|
|
2700
|
+
` - "domain/${rule.domain}"`,
|
|
2701
|
+
"---",
|
|
2702
|
+
"",
|
|
2703
|
+
`# ${title}`,
|
|
2704
|
+
"",
|
|
2705
|
+
`> [!NOTE] \uC778\uACFC\uC728 \uADDC\uCE59 \uBA54\uD0C0\uB370\uC774\uD130`,
|
|
2706
|
+
`> - **\uB3C4\uBA54\uC778**: \`${rule.domain}\``,
|
|
2707
|
+
`> - **\uC2E0\uB8B0\uB3C4**: \`${rule.confidence ?? 1}\` | **\uAC00\uC911\uCE58**: \`${rule.weight ?? 1}\``,
|
|
2708
|
+
`> - **\uCD5C\uC885 \uAC31\uC2E0**: \`${rule.updatedAt || "N/A"}\``,
|
|
2709
|
+
"",
|
|
2710
|
+
"## \uD83D\uDCA1 \uC694\uC57D \uC124\uBA85",
|
|
2711
|
+
rule.description || "\uC0C1\uC138 \uC124\uBA85 \uC5C6\uC74C",
|
|
2712
|
+
"",
|
|
2713
|
+
"## \uD83D\uDEE1\uFE0F \uD544\uC218 \uBD88\uBCC0\uC2DD (Invariants)"
|
|
2714
|
+
];
|
|
2715
|
+
if (!rule.invariants || rule.invariants.length === 0) {
|
|
2716
|
+
lines.push("- *\uB4F1\uB85D\uB41C \uBD88\uBCC0 \uC870\uAC74 \uC5C6\uC74C*");
|
|
2717
|
+
} else {
|
|
2718
|
+
for (const inv of rule.invariants) {
|
|
2719
|
+
lines.push(`- [x] ${inv}`);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
if (rule.pattern) {
|
|
2723
|
+
lines.push("", "## \uD83D\uDD0D \uB9E4\uCE6D \uD328\uD134", `\`\`\`regex
|
|
2724
|
+
${rule.pattern}
|
|
2725
|
+
\`\`\``);
|
|
2726
|
+
}
|
|
2727
|
+
return lines.join(`
|
|
2728
|
+
`);
|
|
2729
|
+
}
|
|
2730
|
+
renderTrapToMarkdown(trap) {
|
|
2731
|
+
const title = trap.title || trap.failedApproach || trap.id;
|
|
2732
|
+
const severity = trap.severity || "WARNING";
|
|
2733
|
+
const rootCause = trap.rootCause || trap.reason || "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
|
|
2734
|
+
const counterMeasure = trap.counterMeasure || trap.safeAlternative || "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
|
|
2735
|
+
const avoidFiles = trap.triggerFiles || trap.avoidFiles || [];
|
|
2736
|
+
return [
|
|
2737
|
+
"---",
|
|
2738
|
+
`id: "${trap.id}"`,
|
|
2739
|
+
`title: "${title.replaceAll('"', "\\\"")}"`,
|
|
2740
|
+
`domain: "${trap.domain || trap.environment || "general"}"`,
|
|
2741
|
+
`severity: "${severity}"`,
|
|
2742
|
+
`createdAt: "${trap.createdAt}"`,
|
|
2743
|
+
"tags:",
|
|
2744
|
+
' - "synapse/anti-pattern"',
|
|
2745
|
+
` - "severity/${severity.toLowerCase()}"`,
|
|
2746
|
+
"---",
|
|
2747
|
+
"",
|
|
2748
|
+
`# \u26A0\uFE0F \uD568\uC815 \uACBD\uBCF4: ${title}`,
|
|
2749
|
+
"",
|
|
2750
|
+
`> [!WARNING] \uC704\uD5D8\uB3C4: **${severity}** (\uB3C4\uBA54\uC778: \`${trap.domain || "general"}\`)`,
|
|
2751
|
+
`> \uC774 \uD328\uD134\uC740 \uC774\uC804\uC5D0 \uC2E4\uD328\uB97C \uC720\uBC1C\uD588\uB358 \uC548\uD2F0\uD328\uD134\uC785\uB2C8\uB2E4. \uB3D9\uC77C\uD55C \uC2DC\uB3C4\uB97C \uCC28\uB2E8\uD574\uC57C \uD569\uB2C8\uB2E4.`,
|
|
2752
|
+
"",
|
|
2753
|
+
"## \uD83D\uDCA5 \uC2E4\uD328 \uC6D0\uC778 (Root Cause)",
|
|
2754
|
+
rootCause,
|
|
2755
|
+
"",
|
|
2756
|
+
"## \u2705 \uC548\uC804\uD55C \uB300\uC548 (Safe Solution)",
|
|
2757
|
+
counterMeasure,
|
|
2758
|
+
"",
|
|
2759
|
+
"## \uD83E\uDEA4 \uAC10\uC9C0 \uD30C\uC77C \uD328\uD134",
|
|
2760
|
+
...avoidFiles.length > 0 ? avoidFiles.map((f) => `- \`${f}\``) : ["- *\uC804\uC5ED \uAC10\uC9C0*"]
|
|
2761
|
+
].join(`
|
|
2762
|
+
`);
|
|
2763
|
+
}
|
|
2764
|
+
renderCheckpointToMarkdown(cp) {
|
|
2765
|
+
return [
|
|
2766
|
+
"---",
|
|
2767
|
+
`id: "${cp.id}"`,
|
|
2768
|
+
`title: "${cp.title.replaceAll('"', "\\\"")}"`,
|
|
2769
|
+
`createdAt: "${cp.createdAt}"`,
|
|
2770
|
+
`cwd: "${cp.cwd.replaceAll("\\", "/")}"`,
|
|
2771
|
+
`branch: "${cp.gitBranch || "unknown"}"`,
|
|
2772
|
+
`commit: "${cp.gitCommit || "unknown"}"`,
|
|
2773
|
+
"tags:",
|
|
2774
|
+
' - "synapse/checkpoint"',
|
|
2775
|
+
"---",
|
|
2776
|
+
"",
|
|
2777
|
+
`# \uD83D\uDCCD \uCCB4\uD06C\uD3EC\uC778\uD2B8: ${cp.title}`,
|
|
2778
|
+
"",
|
|
2779
|
+
`> [!INFO] \uC2A4\uB0C5\uC0F7 \uC815\uBCF4`,
|
|
2780
|
+
`> - **ID**: \`${cp.id}\``,
|
|
2781
|
+
`> - **\uAE30\uB85D \uC2DC\uAC01**: \`${cp.createdAt}\``,
|
|
2782
|
+
`> - **Git \uBE0C\uB79C\uCE58**: \`${cp.gitBranch || "N/A"}\` (\`${cp.gitCommit || "N/A"}\`)`,
|
|
2783
|
+
`> - **\uC791\uC5C5 \uB514\uB809\uD130\uB9AC**: \`${cp.cwd}\``,
|
|
2784
|
+
"",
|
|
2785
|
+
"## \uD83C\uDFAF \uC138\uC158 \uBAA9\uD45C",
|
|
2786
|
+
cp.goal,
|
|
2787
|
+
"",
|
|
2788
|
+
"## \u2705 \uC644\uB8CC\uB41C \uB2E8\uACC4 (Completed Steps)",
|
|
2789
|
+
...cp.completedSteps.map((s) => `- [x] ${s}`),
|
|
2790
|
+
"",
|
|
2791
|
+
"## \u23F3 \uB300\uAE30 \uACFC\uC81C (Pending Tasks)",
|
|
2792
|
+
...cp.pendingTasks.map((t) => `- [ ] ${t}`),
|
|
2793
|
+
"",
|
|
2794
|
+
"## \uD83E\uDDE0 \uD575\uC2EC \uACB0\uC815 \uC0AC\uD56D (Key Decisions)",
|
|
2795
|
+
...cp.keyDecisions.map((d) => `- ${d}`),
|
|
2796
|
+
"",
|
|
2797
|
+
"## \uD83D\uDEE1\uFE0F \uD65C\uC131 \uBD88\uBCC0\uC2DD (Active Invariants)",
|
|
2798
|
+
...cp.activeInvariants.map((inv) => `- \uD83D\uDD12 ${inv}`),
|
|
2799
|
+
"",
|
|
2800
|
+
"## \uD83D\uDCC1 \uC218\uC815\uB41C \uD30C\uC77C \uBAA9\uB85D",
|
|
2801
|
+
...cp.modifiedFiles.map((f) => `- \`${f}\``)
|
|
2802
|
+
].join(`
|
|
2803
|
+
`);
|
|
2804
|
+
}
|
|
2805
|
+
renderTimelineMarkdown() {
|
|
2806
|
+
const events = this.timeSeriesEngine.generateTimeline(50);
|
|
2807
|
+
const lines = [
|
|
2808
|
+
"---",
|
|
2809
|
+
'title: "Synapse Causal Memory Timeline"',
|
|
2810
|
+
"tags:",
|
|
2811
|
+
' - "synapse/timeline"',
|
|
2812
|
+
"---",
|
|
2813
|
+
"",
|
|
2814
|
+
"# \u23F3 \uC2DC\uB0C5\uC2A4 \uC778\uACFC\uC728 \uD0C0\uC784\uB77C\uC778",
|
|
2815
|
+
"",
|
|
2816
|
+
`> \uCD1D **${events.length}**\uAC1C\uC758 \uC774\uBCA4\uD2B8\uAC00 \uBCF4\uC874\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`,
|
|
2817
|
+
"",
|
|
2818
|
+
"## \uD83D\uDCCA \uD0C0\uC784\uB77C\uC778 \uC2DC\uAC01\uD654 (Mermaid)",
|
|
2819
|
+
"```mermaid",
|
|
2820
|
+
"timeline",
|
|
2821
|
+
" title \uC138\uC158 \uC9C4\uD654 \uD0C0\uC784\uB77C\uC778"
|
|
2822
|
+
];
|
|
2823
|
+
for (const ev of events.slice(-10)) {
|
|
2824
|
+
const datePart = ev.timestamp ? ev.timestamp.slice(0, 10) : "2026-11-09";
|
|
2825
|
+
const safeTitle = (ev.title || "\uC774\uBCA4\uD2B8").replaceAll('"', "").replaceAll(":", " -");
|
|
2826
|
+
lines.push(` ${datePart} : ${safeTitle}`);
|
|
2827
|
+
}
|
|
2828
|
+
lines.push("```", "", "## \uD83D\uDCCB \uC774\uBCA4\uD2B8 \uC774\uB825");
|
|
2829
|
+
for (const ev of events) {
|
|
2830
|
+
const targetLink = ev.checkpointId ? `./Checkpoints/${ev.checkpointId}.md` : "#";
|
|
2831
|
+
lines.push(`### [${ev.title}](${targetLink})`);
|
|
2832
|
+
lines.push(`- **\uBC1C\uC0DD \uC77C\uC2DC**: \`${ev.timestamp}\``);
|
|
2833
|
+
lines.push(`- **\uC694\uC57D**: ${ev.summary}`);
|
|
2834
|
+
lines.push("");
|
|
2835
|
+
}
|
|
2836
|
+
return lines.join(`
|
|
2837
|
+
`);
|
|
2838
|
+
}
|
|
2839
|
+
generateCanvasDocument(rules, relations) {
|
|
2840
|
+
const nodes = [];
|
|
2841
|
+
const edges = [];
|
|
2842
|
+
const ruleIdToNodeId = new Map;
|
|
2843
|
+
const nodeWidth = 320;
|
|
2844
|
+
const nodeHeight = 160;
|
|
2845
|
+
const spacingX = 400;
|
|
2846
|
+
const spacingY = 220;
|
|
2847
|
+
const cols = 4;
|
|
2848
|
+
rules.forEach((rule, idx) => {
|
|
2849
|
+
const title = rule.title || rule.name || rule.id;
|
|
2850
|
+
const col = idx % cols;
|
|
2851
|
+
const row = Math.floor(idx / cols);
|
|
2852
|
+
const nodeId = `rule_node_${idx}`;
|
|
2853
|
+
ruleIdToNodeId.set(title.toLowerCase(), nodeId);
|
|
2854
|
+
ruleIdToNodeId.set(rule.id.toLowerCase(), nodeId);
|
|
2855
|
+
const summaryInvariants = rule.invariants && rule.invariants.length > 0 ? rule.invariants.slice(0, 2).map((i) => `\u2022 ${i}`).join(`
|
|
2856
|
+
`) : "\u2022 \uBD88\uBCC0\uC2DD \uC5C6\uC74C";
|
|
2857
|
+
const markdownText = [
|
|
2858
|
+
`### \uD83D\uDCCC [[Rules/${this.sanitizeFileName(title)}|${title}]]`,
|
|
2859
|
+
`**\uB3C4\uBA54\uC778**: \`${rule.domain}\` | **\uAC00\uC911\uCE58**: \`${rule.weight ?? 1}\``,
|
|
2860
|
+
"",
|
|
2861
|
+
summaryInvariants
|
|
2862
|
+
].join(`
|
|
2863
|
+
`);
|
|
2864
|
+
nodes.push({
|
|
2865
|
+
id: nodeId,
|
|
2866
|
+
x: col * spacingX,
|
|
2867
|
+
y: row * spacingY,
|
|
2868
|
+
width: nodeWidth,
|
|
2869
|
+
height: nodeHeight,
|
|
2870
|
+
type: "text",
|
|
2871
|
+
text: markdownText,
|
|
2872
|
+
color: this.getColorForDomain(rule.domain)
|
|
2873
|
+
});
|
|
2874
|
+
});
|
|
2875
|
+
let edgeCounter = 0;
|
|
2876
|
+
for (const rel of relations) {
|
|
2877
|
+
const fromNodeId = ruleIdToNodeId.get(rel.from.toLowerCase());
|
|
2878
|
+
const toNodeId = ruleIdToNodeId.get(rel.to.toLowerCase());
|
|
2879
|
+
if (fromNodeId && toNodeId) {
|
|
2880
|
+
edges.push({
|
|
2881
|
+
id: `edge_${edgeCounter++}`,
|
|
2882
|
+
fromNode: fromNodeId,
|
|
2883
|
+
fromSide: "right",
|
|
2884
|
+
toNode: toNodeId,
|
|
2885
|
+
toSide: "left",
|
|
2886
|
+
label: rel.relation,
|
|
2887
|
+
color: rel.relation === "blocks" ? "1" : "4"
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
return { nodes, edges };
|
|
2892
|
+
}
|
|
2893
|
+
getColorForDomain(domain) {
|
|
2894
|
+
switch (domain.toLowerCase()) {
|
|
2895
|
+
case "security":
|
|
2896
|
+
case "guard":
|
|
2897
|
+
return "1";
|
|
2898
|
+
case "compiler":
|
|
2899
|
+
return "2";
|
|
2900
|
+
case "memory":
|
|
2901
|
+
case "database":
|
|
2902
|
+
return "4";
|
|
2903
|
+
case "runner":
|
|
2904
|
+
case "bus":
|
|
2905
|
+
return "5";
|
|
2906
|
+
case "studio":
|
|
2907
|
+
case "frontend":
|
|
2908
|
+
return "6";
|
|
2909
|
+
default:
|
|
2910
|
+
return "3";
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
parseRuleMarkdown(raw, defaultName) {
|
|
2914
|
+
try {
|
|
2915
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
2916
|
+
if (!fmMatch)
|
|
2917
|
+
return null;
|
|
2918
|
+
const fmText = fmMatch[1];
|
|
2919
|
+
const idMatch = fmText.match(/id:\s*"([^"]+)"/);
|
|
2920
|
+
const nameMatch = fmText.match(/name:\s*"([^"]+)"/);
|
|
2921
|
+
const domainMatch = fmText.match(/domain:\s*"([^"]+)"/);
|
|
2922
|
+
const confMatch = fmText.match(/confidence:\s*([0-9.]+)/);
|
|
2923
|
+
const weightMatch = fmText.match(/weight:\s*([0-9.]+)/);
|
|
2924
|
+
const invariants = [];
|
|
2925
|
+
const invSection = fmText.match(/invariants:\r?\n((?:\s*-\s*"[^"]+"\r?\n?)+)/);
|
|
2926
|
+
if (invSection) {
|
|
2927
|
+
const itemMatches = invSection[1].matchAll(/-\s*"([^"]+)"/g);
|
|
2928
|
+
for (const m of itemMatches) {
|
|
2929
|
+
invariants.push(m[1].replaceAll("\\\"", '"'));
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
const body = raw.slice(fmMatch[0].length).trim();
|
|
2933
|
+
const descMatch = body.match(/## \uD83D\uDCA1 \uC694\uC57D \uC124\uBA85\r?\n([\s\S]*?)(?:\r?\n##|$)/);
|
|
2934
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
2935
|
+
const ruleTitle = nameMatch ? nameMatch[1] : defaultName;
|
|
2936
|
+
return {
|
|
2937
|
+
id: idMatch ? idMatch[1] : `rule_${Date.now()}`,
|
|
2938
|
+
title: ruleTitle,
|
|
2939
|
+
name: ruleTitle,
|
|
2940
|
+
domain: domainMatch ? domainMatch[1] : "general",
|
|
2941
|
+
confidence: confMatch ? parseFloat(confMatch[1]) : 1,
|
|
2942
|
+
weight: weightMatch ? parseFloat(weightMatch[1]) : 1,
|
|
2943
|
+
triggers: {
|
|
2944
|
+
files: [],
|
|
2945
|
+
keywords: [ruleTitle.toLowerCase()]
|
|
2946
|
+
},
|
|
2947
|
+
invariants: invariants || [],
|
|
2948
|
+
actions: [],
|
|
2949
|
+
context: description ? [description] : [],
|
|
2950
|
+
description,
|
|
2951
|
+
updatedAt: new Date().toISOString()
|
|
2952
|
+
};
|
|
2953
|
+
} catch {
|
|
2954
|
+
return null;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
parseTrapMarkdown(raw, defaultPattern) {
|
|
2958
|
+
try {
|
|
2959
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
2960
|
+
if (!fmMatch)
|
|
2961
|
+
return null;
|
|
2962
|
+
const fmText = fmMatch[1];
|
|
2963
|
+
const idMatch = fmText.match(/id:\s*"([^"]+)"/);
|
|
2964
|
+
const titleMatch = fmText.match(/(?:title|trapPattern):\s*"([^"]+)"/);
|
|
2965
|
+
const domainMatch = fmText.match(/domain:\s*"([^"]+)"/);
|
|
2966
|
+
const sevMatch = fmText.match(/severity:\s*"([^"]+)"/);
|
|
2967
|
+
const body = raw.slice(fmMatch[0].length).trim();
|
|
2968
|
+
const reasonMatch = body.match(/## \uD83D\uDCA5 \uC2E4\uD328 \uC6D0\uC778 \(Root Cause\)\r?\n([\s\S]*?)(?:\r?\n##|$)/);
|
|
2969
|
+
const altMatch = body.match(/## \u2705 \uC548\uC804\uD55C \uB300\uC548 \(Safe Solution\)\r?\n([\s\S]*?)(?:\r?\n##|$)/);
|
|
2970
|
+
const trapTitle = titleMatch ? titleMatch[1] : defaultPattern;
|
|
2971
|
+
const rootCause = reasonMatch ? reasonMatch[1].trim() : "\uC0AC\uC720 \uBBF8\uAE30\uC7AC";
|
|
2972
|
+
const counterMeasure = altMatch ? altMatch[1].trim() : "\uC548\uC804\uD55C \uB300\uC548 \uBBF8\uAE30\uC7AC";
|
|
2973
|
+
return {
|
|
2974
|
+
id: idMatch ? idMatch[1] : `trap_${Date.now()}`,
|
|
2975
|
+
title: trapTitle,
|
|
2976
|
+
failedApproach: trapTitle,
|
|
2977
|
+
trapPattern: trapTitle,
|
|
2978
|
+
domain: domainMatch ? domainMatch[1] : "general",
|
|
2979
|
+
environment: domainMatch ? domainMatch[1] : "general",
|
|
2980
|
+
severity: sevMatch ? sevMatch[1] : "MEDIUM",
|
|
2981
|
+
rootCause,
|
|
2982
|
+
reason: rootCause,
|
|
2983
|
+
counterMeasure,
|
|
2984
|
+
safeAlternative: counterMeasure,
|
|
2985
|
+
triggerKeywords: [trapTitle],
|
|
2986
|
+
createdAt: new Date().toISOString()
|
|
2987
|
+
};
|
|
2988
|
+
} catch {
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
atomicWriteFile(filePath, content) {
|
|
2993
|
+
const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
2994
|
+
writeFileSync5(tempPath, content, "utf-8");
|
|
2995
|
+
renameSync4(tempPath, filePath);
|
|
2996
|
+
}
|
|
2997
|
+
sanitizeFileName(name) {
|
|
2998
|
+
return name.replace(/\.\.+/g, "_").replace(/[\\/:*?"<>|\0]/g, "_").replace(/\s+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "unnamed";
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
var init_causal_lens = __esm(() => {
|
|
3002
|
+
init_causal_config();
|
|
3003
|
+
init_causal_store();
|
|
3004
|
+
init_causal_context();
|
|
3005
|
+
init_causal_graveyard();
|
|
3006
|
+
init_causal_timeseries();
|
|
3007
|
+
});
|
|
3008
|
+
|
|
1041
3009
|
// causal-server.ts
|
|
1042
3010
|
import { createInterface } from "readline";
|
|
1043
|
-
async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
|
|
3011
|
+
async function handleMcpJsonRpcRequest(rpc, store, dbPath, contextDbPath) {
|
|
1044
3012
|
const { id, method, params } = rpc;
|
|
3013
|
+
const contextStore = new ContextCheckpointStore(contextDbPath, dbPath);
|
|
1045
3014
|
if (method.startsWith("notifications/")) {
|
|
1046
3015
|
return null;
|
|
1047
3016
|
}
|
|
@@ -1079,6 +3048,7 @@ async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
|
|
|
1079
3048
|
}
|
|
1080
3049
|
if (method === "tools/call") {
|
|
1081
3050
|
store.reloadIfNeeded();
|
|
3051
|
+
contextStore.reloadIfNeeded();
|
|
1082
3052
|
const toolName = params?.name;
|
|
1083
3053
|
const args = params?.arguments || {};
|
|
1084
3054
|
try {
|
|
@@ -1098,6 +3068,12 @@ async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
|
|
|
1098
3068
|
maxHops: args.maxHops
|
|
1099
3069
|
});
|
|
1100
3070
|
payloadText = formatResonanceForAgent(res, Boolean(args.compact));
|
|
3071
|
+
const latestCtx = contextStore.getLatestCheckpoint(typeof args.sessionId === "string" ? args.sessionId : undefined);
|
|
3072
|
+
if (latestCtx && !args.compact && args.includeContext !== false) {
|
|
3073
|
+
payloadText += `
|
|
3074
|
+
|
|
3075
|
+
` + ContextCheckpointStore.formatForAgentResume(latestCtx, false);
|
|
3076
|
+
}
|
|
1101
3077
|
break;
|
|
1102
3078
|
}
|
|
1103
3079
|
case "assert_causal_rule": {
|
|
@@ -1112,13 +3088,60 @@ async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
|
|
|
1112
3088
|
case "audit_causal_memory": {
|
|
1113
3089
|
const audit = store.audit();
|
|
1114
3090
|
const status = store.getStatus();
|
|
1115
|
-
|
|
3091
|
+
const checkpointsCount = contextStore.listCheckpoints().length;
|
|
3092
|
+
payloadText = JSON.stringify({ ...audit, status, checkpointsCount }, null, 2);
|
|
1116
3093
|
break;
|
|
1117
3094
|
}
|
|
1118
3095
|
case "read_graph": {
|
|
1119
3096
|
const gitFiles = await detectModifiedFiles();
|
|
1120
3097
|
const smartGraph = store.readSmartGraph(gitFiles);
|
|
1121
|
-
|
|
3098
|
+
const latestCheckpoint = contextStore.getLatestCheckpoint();
|
|
3099
|
+
payloadText = JSON.stringify({
|
|
3100
|
+
...smartGraph,
|
|
3101
|
+
latestContextCheckpoint: latestCheckpoint || null
|
|
3102
|
+
}, null, 2);
|
|
3103
|
+
break;
|
|
3104
|
+
}
|
|
3105
|
+
case "save_context_checkpoint": {
|
|
3106
|
+
const saved = await contextStore.saveCheckpoint(args);
|
|
3107
|
+
payloadText = JSON.stringify({
|
|
3108
|
+
ok: true,
|
|
3109
|
+
message: `\uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC800\uC7A5 \uC644\uB8CC: ${saved.id}`,
|
|
3110
|
+
checkpoint: saved
|
|
3111
|
+
}, null, 2);
|
|
3112
|
+
break;
|
|
3113
|
+
}
|
|
3114
|
+
case "load_context_checkpoint": {
|
|
3115
|
+
const targetId = typeof args.id === "string" ? args.id : "latest";
|
|
3116
|
+
const sessionId = typeof args.sessionId === "string" ? args.sessionId : undefined;
|
|
3117
|
+
const found = contextStore.getCheckpoint(targetId, sessionId);
|
|
3118
|
+
if (!found) {
|
|
3119
|
+
payloadText = JSON.stringify({
|
|
3120
|
+
ok: false,
|
|
3121
|
+
message: "\uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
|
|
3122
|
+
});
|
|
3123
|
+
} else {
|
|
3124
|
+
payloadText = ContextCheckpointStore.formatForAgentResume(found, Boolean(args.compact));
|
|
3125
|
+
}
|
|
3126
|
+
break;
|
|
3127
|
+
}
|
|
3128
|
+
case "list_context_checkpoints": {
|
|
3129
|
+
const sessionId = typeof args.sessionId === "string" ? args.sessionId : undefined;
|
|
3130
|
+
const limit = typeof args.limit === "number" ? args.limit : 20;
|
|
3131
|
+
const list = contextStore.listCheckpoints({ sessionId, limit });
|
|
3132
|
+
payloadText = JSON.stringify({
|
|
3133
|
+
ok: true,
|
|
3134
|
+
totalCount: list.length,
|
|
3135
|
+
checkpoints: list.map((c) => ({
|
|
3136
|
+
id: c.id,
|
|
3137
|
+
sessionId: c.sessionId,
|
|
3138
|
+
title: c.title,
|
|
3139
|
+
createdAt: c.createdAt,
|
|
3140
|
+
goal: c.goal,
|
|
3141
|
+
pendingTasksCount: c.pendingTasks.length,
|
|
3142
|
+
completedStepsCount: c.completedSteps.length
|
|
3143
|
+
}))
|
|
3144
|
+
}, null, 2);
|
|
1122
3145
|
break;
|
|
1123
3146
|
}
|
|
1124
3147
|
case "search_nodes": {
|
|
@@ -1168,6 +3191,102 @@ async function handleMcpJsonRpcRequest(rpc, store, dbPath) {
|
|
|
1168
3191
|
payloadText = "Relations deleted successfully";
|
|
1169
3192
|
break;
|
|
1170
3193
|
}
|
|
3194
|
+
case "time_travel_causal_memory": {
|
|
3195
|
+
const timeSeries = new CausalTimeSeriesEngine(contextStore, store);
|
|
3196
|
+
if (args.action === "diff") {
|
|
3197
|
+
const diff = timeSeries.diffCheckpoints(String(args.fromId || ""), String(args.toId || ""));
|
|
3198
|
+
payloadText = JSON.stringify(diff, null, 2);
|
|
3199
|
+
} else if (args.action === "rewind") {
|
|
3200
|
+
const res = await timeSeries.rewindToCheckpoint(String(args.targetId || ""));
|
|
3201
|
+
payloadText = JSON.stringify(res, null, 2);
|
|
3202
|
+
} else if (args.action === "timeline") {
|
|
3203
|
+
const tl = timeSeries.generateTimeline(typeof args.limit === "number" ? args.limit : 20);
|
|
3204
|
+
payloadText = JSON.stringify(tl, null, 2);
|
|
3205
|
+
} else {
|
|
3206
|
+
const resonance = store.queryResonance({
|
|
3207
|
+
files: Array.isArray(args.files) ? args.files : []
|
|
3208
|
+
});
|
|
3209
|
+
const decayed = timeSeries.computeDecayedResonance(resonance.resonanceRules);
|
|
3210
|
+
payloadText = JSON.stringify(decayed, null, 2);
|
|
3211
|
+
}
|
|
3212
|
+
break;
|
|
3213
|
+
}
|
|
3214
|
+
case "scan_causal_graveyard": {
|
|
3215
|
+
const graveyard = new CausalGraveyardStore(store);
|
|
3216
|
+
if (args.action === "assert") {
|
|
3217
|
+
const trap = graveyard.assertTrap(args);
|
|
3218
|
+
payloadText = JSON.stringify({ ok: true, trap }, null, 2);
|
|
3219
|
+
} else if (args.action === "list") {
|
|
3220
|
+
const list = graveyard.getAllTraps();
|
|
3221
|
+
payloadText = JSON.stringify({ ok: true, traps: list }, null, 2);
|
|
3222
|
+
} else {
|
|
3223
|
+
const res = graveyard.scanTraps({
|
|
3224
|
+
files: Array.isArray(args.files) ? args.files : undefined,
|
|
3225
|
+
keywords: Array.isArray(args.keywords) ? args.keywords : undefined
|
|
3226
|
+
});
|
|
3227
|
+
payloadText = CausalGraveyardStore.formatTrapWarnings(res);
|
|
3228
|
+
}
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
case "branch_causal_context": {
|
|
3232
|
+
const branchStore = new CausalBranchStore(contextStore);
|
|
3233
|
+
if (args.action === "create") {
|
|
3234
|
+
const b = await branchStore.createBranch(String(args.name || ""), {
|
|
3235
|
+
description: args.fromBranch
|
|
3236
|
+
});
|
|
3237
|
+
payloadText = JSON.stringify({ ok: true, branch: b }, null, 2);
|
|
3238
|
+
} else if (args.action === "switch") {
|
|
3239
|
+
const b = branchStore.switchBranch(String(args.name || ""));
|
|
3240
|
+
payloadText = JSON.stringify({ ok: true, branch: b }, null, 2);
|
|
3241
|
+
} else if (args.action === "merge") {
|
|
3242
|
+
const res = await branchStore.mergeBranch(String(args.name || ""), args.targetBranch);
|
|
3243
|
+
payloadText = JSON.stringify(res, null, 2);
|
|
3244
|
+
} else if (args.action === "abandon") {
|
|
3245
|
+
const res = branchStore.abandonBranch(String(args.name || ""), String(args.failureReason || "\uD3D0\uAE30\uB428"), args.safeAlternative);
|
|
3246
|
+
payloadText = JSON.stringify(res, null, 2);
|
|
3247
|
+
} else {
|
|
3248
|
+
const branches = branchStore.listBranches();
|
|
3249
|
+
const current = branchStore.getCurrentBranch();
|
|
3250
|
+
payloadText = JSON.stringify({ ok: true, currentBranch: current, branches }, null, 2);
|
|
3251
|
+
}
|
|
3252
|
+
break;
|
|
3253
|
+
}
|
|
3254
|
+
case "predict_blast_radius": {
|
|
3255
|
+
const predictor = new CausalPredictorEngine(store, contextStore);
|
|
3256
|
+
const files = Array.isArray(args.files) ? args.files : [];
|
|
3257
|
+
const maxHops = typeof args.maxHops === "number" ? args.maxHops : 3;
|
|
3258
|
+
const report = predictor.analyzeBlastRadius(files, maxHops);
|
|
3259
|
+
payloadText = JSON.stringify(report, null, 2);
|
|
3260
|
+
break;
|
|
3261
|
+
}
|
|
3262
|
+
case "synthesize_git_checkpoint": {
|
|
3263
|
+
const synthesizer = new CausalSynthesizerEngine(contextStore, store);
|
|
3264
|
+
const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
|
|
3265
|
+
if (args.autoCheckpoint === false) {
|
|
3266
|
+
const summary = await synthesizer.inspectGitChanges(cwd);
|
|
3267
|
+
const intent = synthesizer.synthesizeIntent(summary, args.title);
|
|
3268
|
+
payloadText = JSON.stringify({ summary, intent }, null, 2);
|
|
3269
|
+
} else {
|
|
3270
|
+
const cp = await synthesizer.autoCheckpoint(cwd, { title: args.title });
|
|
3271
|
+
payloadText = JSON.stringify({ ok: true, checkpoint: cp }, null, 2);
|
|
3272
|
+
}
|
|
3273
|
+
break;
|
|
3274
|
+
}
|
|
3275
|
+
case "sync_obsidian_lens": {
|
|
3276
|
+
const lens = new CausalObsidianLensEngine(store, contextStore);
|
|
3277
|
+
const vaultPath = typeof args.vaultPath === "string" ? args.vaultPath : undefined;
|
|
3278
|
+
if (args.action === "export") {
|
|
3279
|
+
const exp = await lens.exportToVault(vaultPath);
|
|
3280
|
+
payloadText = JSON.stringify(exp, null, 2);
|
|
3281
|
+
} else if (args.action === "import") {
|
|
3282
|
+
const imp = await lens.importFromVault(vaultPath);
|
|
3283
|
+
payloadText = JSON.stringify(imp, null, 2);
|
|
3284
|
+
} else {
|
|
3285
|
+
const syncRes = await lens.syncWithVault(vaultPath);
|
|
3286
|
+
payloadText = JSON.stringify(syncRes, null, 2);
|
|
3287
|
+
}
|
|
3288
|
+
break;
|
|
3289
|
+
}
|
|
1171
3290
|
default:
|
|
1172
3291
|
return {
|
|
1173
3292
|
jsonrpc: "2.0",
|
|
@@ -1211,6 +3330,7 @@ function startCausalServer(options = {}) {
|
|
|
1211
3330
|
const port = options.port || CAUSAL_MEMORY_CONFIG.DEFAULT_PORT;
|
|
1212
3331
|
const host = options.host || CAUSAL_MEMORY_CONFIG.DEFAULT_HOST;
|
|
1213
3332
|
const store = new CausalMemoryStore(options.dbPath);
|
|
3333
|
+
const contextStore = new ContextCheckpointStore(options.contextDbPath, options.dbPath);
|
|
1214
3334
|
const server = Bun.serve({
|
|
1215
3335
|
port,
|
|
1216
3336
|
hostname: host,
|
|
@@ -1287,9 +3407,117 @@ function startCausalServer(options = {}) {
|
|
|
1287
3407
|
const mergeResult = store.mergeDatabase(incomingRules, incomingRelations);
|
|
1288
3408
|
return new Response(JSON.stringify({ ok: true, ...mergeResult }), { headers });
|
|
1289
3409
|
}
|
|
3410
|
+
if (url.pathname === "/context" && req.method === "GET") {
|
|
3411
|
+
contextStore.reloadIfNeeded();
|
|
3412
|
+
const id = url.searchParams.get("id") || undefined;
|
|
3413
|
+
const sessionId = url.searchParams.get("sessionId") || undefined;
|
|
3414
|
+
const listMode = url.searchParams.get("list") === "true";
|
|
3415
|
+
if (listMode) {
|
|
3416
|
+
const list = contextStore.listCheckpoints({ sessionId });
|
|
3417
|
+
return new Response(JSON.stringify({ ok: true, checkpoints: list }), { headers });
|
|
3418
|
+
}
|
|
3419
|
+
const found = contextStore.getCheckpoint(id || "latest", sessionId);
|
|
3420
|
+
return new Response(JSON.stringify({ ok: Boolean(found), checkpoint: found }), { headers });
|
|
3421
|
+
}
|
|
3422
|
+
if (url.pathname === "/context" && req.method === "POST") {
|
|
3423
|
+
const body = await req.json();
|
|
3424
|
+
const saved = await contextStore.saveCheckpoint(body);
|
|
3425
|
+
return new Response(JSON.stringify({ ok: true, checkpoint: saved }), { headers });
|
|
3426
|
+
}
|
|
3427
|
+
if (url.pathname === "/timeline" && req.method === "GET") {
|
|
3428
|
+
const timeSeries = new CausalTimeSeriesEngine(contextStore, store);
|
|
3429
|
+
const limit = parseInt(url.searchParams.get("limit") || "20", 10);
|
|
3430
|
+
const tl = timeSeries.generateTimeline(limit);
|
|
3431
|
+
return new Response(JSON.stringify({ ok: true, timeline: tl }), { headers });
|
|
3432
|
+
}
|
|
3433
|
+
if (url.pathname === "/graveyard") {
|
|
3434
|
+
const graveyard = new CausalGraveyardStore(store);
|
|
3435
|
+
if (req.method === "GET") {
|
|
3436
|
+
const list = graveyard.getAllTraps();
|
|
3437
|
+
return new Response(JSON.stringify({ ok: true, traps: list }), { headers });
|
|
3438
|
+
}
|
|
3439
|
+
if (req.method === "POST") {
|
|
3440
|
+
const body = await req.json();
|
|
3441
|
+
if (body.action === "scan") {
|
|
3442
|
+
const scanRes = graveyard.scanTraps({
|
|
3443
|
+
files: body.files,
|
|
3444
|
+
keywords: body.keywords
|
|
3445
|
+
});
|
|
3446
|
+
return new Response(JSON.stringify({ ok: true, scan: scanRes }), { headers });
|
|
3447
|
+
}
|
|
3448
|
+
const trap = graveyard.assertTrap(body);
|
|
3449
|
+
return new Response(JSON.stringify({ ok: true, trap }), { headers });
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
if (url.pathname === "/branch") {
|
|
3453
|
+
const branchStore = new CausalBranchStore(contextStore);
|
|
3454
|
+
if (req.method === "GET") {
|
|
3455
|
+
return new Response(JSON.stringify({
|
|
3456
|
+
ok: true,
|
|
3457
|
+
current: branchStore.getCurrentBranch(),
|
|
3458
|
+
branches: branchStore.listBranches()
|
|
3459
|
+
}), { headers });
|
|
3460
|
+
}
|
|
3461
|
+
if (req.method === "POST") {
|
|
3462
|
+
const body = await req.json();
|
|
3463
|
+
if (body.action === "create") {
|
|
3464
|
+
const b = await branchStore.createBranch(body.name, {
|
|
3465
|
+
description: body.fromBranch
|
|
3466
|
+
});
|
|
3467
|
+
return new Response(JSON.stringify({ ok: true, branch: b }), { headers });
|
|
3468
|
+
}
|
|
3469
|
+
if (body.action === "switch") {
|
|
3470
|
+
const b = branchStore.switchBranch(body.name);
|
|
3471
|
+
return new Response(JSON.stringify({ ok: true, branch: b }), { headers });
|
|
3472
|
+
}
|
|
3473
|
+
if (body.action === "merge") {
|
|
3474
|
+
const res = await branchStore.mergeBranch(body.name, body.targetBranch);
|
|
3475
|
+
return new Response(JSON.stringify(res), { headers });
|
|
3476
|
+
}
|
|
3477
|
+
if (body.action === "abandon") {
|
|
3478
|
+
const res = branchStore.abandonBranch(body.name, body.failureReason, body.safeAlternative);
|
|
3479
|
+
return new Response(JSON.stringify(res), { headers });
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
if (url.pathname === "/predict" && req.method === "POST") {
|
|
3484
|
+
const predictor = new CausalPredictorEngine(store, contextStore);
|
|
3485
|
+
const body = await req.json();
|
|
3486
|
+
const files = Array.isArray(body.files) ? body.files : [];
|
|
3487
|
+
const maxHops = typeof body.maxHops === "number" ? body.maxHops : 3;
|
|
3488
|
+
const report = predictor.analyzeBlastRadius(files, maxHops);
|
|
3489
|
+
return new Response(JSON.stringify({ ok: true, report }), { headers });
|
|
3490
|
+
}
|
|
3491
|
+
if (url.pathname === "/synthesize" && req.method === "POST") {
|
|
3492
|
+
const synthesizer = new CausalSynthesizerEngine(contextStore, store);
|
|
3493
|
+
const body = await req.json();
|
|
3494
|
+
const cwd = typeof body.cwd === "string" ? body.cwd : process.cwd();
|
|
3495
|
+
if (body.autoCheckpoint === false) {
|
|
3496
|
+
const summary = await synthesizer.inspectGitChanges(cwd);
|
|
3497
|
+
const intent = synthesizer.synthesizeIntent(summary, body.title);
|
|
3498
|
+
return new Response(JSON.stringify({ ok: true, summary, intent }), { headers });
|
|
3499
|
+
}
|
|
3500
|
+
const cp = await synthesizer.autoCheckpoint(cwd, { title: body.title });
|
|
3501
|
+
return new Response(JSON.stringify({ ok: true, checkpoint: cp }), { headers });
|
|
3502
|
+
}
|
|
3503
|
+
if (url.pathname === "/lens" && req.method === "POST") {
|
|
3504
|
+
const lens = new CausalObsidianLensEngine(store, contextStore);
|
|
3505
|
+
const body = await req.json();
|
|
3506
|
+
const vaultPath = typeof body.vaultPath === "string" ? body.vaultPath : undefined;
|
|
3507
|
+
if (body.action === "export") {
|
|
3508
|
+
const res = await lens.exportToVault(vaultPath);
|
|
3509
|
+
return new Response(JSON.stringify(res), { headers });
|
|
3510
|
+
}
|
|
3511
|
+
if (body.action === "import") {
|
|
3512
|
+
const res = await lens.importFromVault(vaultPath);
|
|
3513
|
+
return new Response(JSON.stringify(res), { headers });
|
|
3514
|
+
}
|
|
3515
|
+
const syncRes = await lens.syncWithVault(vaultPath);
|
|
3516
|
+
return new Response(JSON.stringify(syncRes), { headers });
|
|
3517
|
+
}
|
|
1290
3518
|
if (url.pathname === "/mcp" && req.method === "POST") {
|
|
1291
3519
|
const rpc = await req.json();
|
|
1292
|
-
const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath);
|
|
3520
|
+
const responseObj = await handleMcpJsonRpcRequest(rpc, store, options.dbPath, options.contextDbPath);
|
|
1293
3521
|
if (!responseObj) {
|
|
1294
3522
|
return new Response(null, { status: 204, headers });
|
|
1295
3523
|
}
|
|
@@ -1359,6 +3587,13 @@ var init_causal_server = __esm(() => {
|
|
|
1359
3587
|
init_causal_config();
|
|
1360
3588
|
init_causal_store();
|
|
1361
3589
|
init_causal_query();
|
|
3590
|
+
init_causal_context();
|
|
3591
|
+
init_causal_timeseries();
|
|
3592
|
+
init_causal_graveyard();
|
|
3593
|
+
init_causal_branch();
|
|
3594
|
+
init_causal_predictor();
|
|
3595
|
+
init_causal_synthesizer();
|
|
3596
|
+
init_causal_lens();
|
|
1362
3597
|
CAUSAL_MCP_TOOLS = [
|
|
1363
3598
|
{
|
|
1364
3599
|
name: "query_causal_memory",
|
|
@@ -1525,52 +3760,235 @@ var init_causal_server = __esm(() => {
|
|
|
1525
3760
|
type: "object",
|
|
1526
3761
|
required: ["entityNames"],
|
|
1527
3762
|
properties: {
|
|
1528
|
-
entityNames: {
|
|
3763
|
+
entityNames: {
|
|
3764
|
+
type: "array",
|
|
3765
|
+
items: { type: "string" }
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
},
|
|
3770
|
+
{
|
|
3771
|
+
name: "delete_observations",
|
|
3772
|
+
description: "Delete specific observations from entities/rules in the causal memory",
|
|
3773
|
+
inputSchema: {
|
|
3774
|
+
type: "object",
|
|
3775
|
+
required: ["deletions"],
|
|
3776
|
+
properties: {
|
|
3777
|
+
deletions: {
|
|
3778
|
+
type: "array",
|
|
3779
|
+
items: {
|
|
3780
|
+
type: "object",
|
|
3781
|
+
required: ["entityName", "observations"],
|
|
3782
|
+
properties: {
|
|
3783
|
+
entityName: { type: "string" },
|
|
3784
|
+
observations: { type: "array", items: { type: "string" } }
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
},
|
|
3791
|
+
{
|
|
3792
|
+
name: "delete_relations",
|
|
3793
|
+
description: "Delete multiple causal relations from the knowledge graph",
|
|
3794
|
+
inputSchema: {
|
|
3795
|
+
type: "object",
|
|
3796
|
+
required: ["relations"],
|
|
3797
|
+
properties: {
|
|
3798
|
+
relations: {
|
|
3799
|
+
type: "array",
|
|
3800
|
+
items: {
|
|
3801
|
+
type: "object",
|
|
3802
|
+
required: ["from", "to", "relationType"],
|
|
3803
|
+
properties: {
|
|
3804
|
+
from: { type: "string" },
|
|
3805
|
+
to: { type: "string" },
|
|
3806
|
+
relationType: { type: "string" }
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
},
|
|
3813
|
+
{
|
|
3814
|
+
name: "save_context_checkpoint",
|
|
3815
|
+
description: "\uD604\uC7AC \uC791\uC5C5 \uC138\uC158\uC758 \uD575\uC2EC \uCEE8\uD14D\uC2A4\uD2B8(\uBAA9\uD45C, \uC644\uB8CC \uB2E8\uACC4, \uB2E4\uC74C \uD560 \uC77C, \uD575\uC2EC \uC124\uACC4 \uACB0\uC815, \uD65C\uC131 \uD30C\uC77C, \uC544\uD2F0\uD329\uD2B8)\uB97C \uCD08\uACBD\uB7C9 \uC2A4\uB0C5\uC0F7\uC73C\uB85C \uC800\uC7A5\uD558\uC5EC \uC0C8 \uCC44\uD305\uCC3D\uC5D0\uC11C\uB3C4 \uAE30\uC5B5 \uC190\uC2E4 \uC5C6\uC774 0ms \uBCF5\uC6D0\uD560 \uC218 \uC788\uAC8C \uD569\uB2C8\uB2E4.",
|
|
3816
|
+
inputSchema: {
|
|
3817
|
+
type: "object",
|
|
3818
|
+
required: ["title"],
|
|
3819
|
+
properties: {
|
|
3820
|
+
id: { type: "string", description: "\uACE0\uC720 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (\uC0DD\uB7B5 \uC2DC \uC790\uB3D9 \uC0DD\uC131)" },
|
|
3821
|
+
sessionId: { type: "string", description: "\uD504\uB85C\uC81D\uD2B8 \uB610\uB294 \uC138\uC158 \uC2DD\uBCC4\uC790 (\uC608: synapse-kit)" },
|
|
3822
|
+
title: { type: "string", description: "\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7 \uC694\uC57D \uC81C\uBAA9" },
|
|
3823
|
+
goal: { type: "string", description: "\uD604\uC7AC \uB2EC\uC131 \uC911\uC778 \uD575\uC2EC \uBAA9\uD45C" },
|
|
3824
|
+
completedSteps: {
|
|
3825
|
+
type: "array",
|
|
3826
|
+
items: { type: "string" },
|
|
3827
|
+
description: "\uC774\uBC88 \uC138\uC158\uAE4C\uC9C0 \uC644\uB8CC\uB41C \uD575\uC2EC \uC791\uC5C5 \uBAA9\uB85D"
|
|
3828
|
+
},
|
|
3829
|
+
pendingTasks: {
|
|
3830
|
+
type: "array",
|
|
3831
|
+
items: { type: "string" },
|
|
3832
|
+
description: "\uB2E4\uC74C \uCC44\uD305/\uC138\uC158\uC5D0\uC11C \uBC14\uB85C \uC774\uC5B4\uC11C \uC9C4\uD589\uD560 \uD560 \uC77C \uBAA9\uB85D"
|
|
3833
|
+
},
|
|
3834
|
+
keyDecisions: {
|
|
3835
|
+
type: "array",
|
|
3836
|
+
items: { type: "string" },
|
|
3837
|
+
description: "\uD575\uC2EC \uC124\uACC4 \uACB0\uC815 \uBC0F \uC544\uD0A4\uD14D\uCC98 \uC8FC\uC758\uC0AC\uD56D"
|
|
3838
|
+
},
|
|
3839
|
+
activeFiles: {
|
|
3840
|
+
type: "array",
|
|
3841
|
+
items: { type: "string" },
|
|
3842
|
+
description: "\uC9D1\uC911 \uC791\uC5C5 \uC911\uC778 \uD30C\uC77C \uACBD\uB85C \uBAA9\uB85D"
|
|
3843
|
+
},
|
|
3844
|
+
artifacts: {
|
|
3845
|
+
type: "array",
|
|
3846
|
+
items: { type: "string" },
|
|
3847
|
+
description: "\uAD00\uB828 \uC544\uD2F0\uD329\uD2B8 \uB610\uB294 \uB9C8\uC2A4\uD130 \uC124\uACC4 \uBB38\uC11C \uACBD\uB85C \uBAA9\uB85D"
|
|
3848
|
+
},
|
|
3849
|
+
cwd: { type: "string", description: "\uC791\uC5C5 \uB514\uB809\uD130\uB9AC \uACBD\uB85C" }
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
},
|
|
3853
|
+
{
|
|
3854
|
+
name: "load_context_checkpoint",
|
|
3855
|
+
description: "\uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC2A4\uB0C5\uC0F7(\uCD5C\uC2E0 \uB610\uB294 \uD2B9\uC815 ID/\uC138\uC158)\uC744 0ms \uB9CC\uC5D0 \uBCF5\uC6D0\uD558\uC5EC \uC0C8 \uCC44\uD305\uCC3D\uC5D0\uC11C \uC774\uC804 \uC791\uC5C5\uC744 \uC989\uC2DC \uC774\uC5B4\uAC11\uB2C8\uB2E4.",
|
|
3856
|
+
inputSchema: {
|
|
3857
|
+
type: "object",
|
|
3858
|
+
properties: {
|
|
3859
|
+
id: {
|
|
3860
|
+
type: "string",
|
|
3861
|
+
description: "\uBCF5\uC6D0\uD560 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID, \uC81C\uBAA9 \uD0A4\uC6CC\uB4DC, \uB610\uB294 'latest' (\uAE30\uBCF8\uAC12: 'latest')"
|
|
3862
|
+
},
|
|
3863
|
+
sessionId: {
|
|
3864
|
+
type: "string",
|
|
3865
|
+
description: "\uD2B9\uC815 \uC138\uC158/\uD504\uB85C\uC81D\uD2B8\uC758 \uCD5C\uC2E0 \uC2A4\uB0C5\uC0F7\uB9CC \uD544\uD130\uB9C1\uD560 \uACBD\uC6B0 \uC9C0\uC815"
|
|
3866
|
+
},
|
|
3867
|
+
compact: {
|
|
3868
|
+
type: "boolean",
|
|
3869
|
+
description: "\uCD08\uC18C\uD615 JSON \uD3EC\uB9F7\uC73C\uB85C \uBC18\uD658\uD560\uC9C0 \uC5EC\uBD80"
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
},
|
|
3874
|
+
{
|
|
3875
|
+
name: "list_context_checkpoints",
|
|
3876
|
+
description: "\uC800\uC7A5\uB41C \uBAA8\uB4E0 \uC791\uC5C5 \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7(Checkpoint) \uBAA9\uB85D\uC744 \uCD5C\uC2E0\uC21C\uC73C\uB85C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
3877
|
+
inputSchema: {
|
|
3878
|
+
type: "object",
|
|
3879
|
+
properties: {
|
|
3880
|
+
sessionId: { type: "string", description: "\uD2B9\uC815 \uC138\uC158/\uD504\uB85C\uC81D\uD2B8 \uD544\uD130" },
|
|
3881
|
+
limit: { type: "number", description: "\uC870\uD68C\uD560 \uCD5C\uB300 \uAC1C\uC218 (\uAE30\uBCF8\uAC12: 20)" }
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
},
|
|
3885
|
+
{
|
|
3886
|
+
name: "time_travel_causal_memory",
|
|
3887
|
+
description: "\uC778\uACFC\uC728 \uC2DC\uACC4\uC5F4(Time-Series) \uC5D4\uC9C4\uC744 \uD1B5\uD574 \uC2DC\uAC04 \uAC10\uC1E0 \uAC00\uC911\uCE58 \uACC4\uC0B0, \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uAC04 \uBCC0\uACBD diff \uBD84\uC11D, \uD2B9\uC815 \uACFC\uAC70 \uC2DC\uC810\uC73C\uB85C\uC758 \uB9AC\uC640\uC778\uB4DC, \uC138\uC158 \uD0C0\uC784\uB77C\uC778\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4.",
|
|
3888
|
+
inputSchema: {
|
|
3889
|
+
type: "object",
|
|
3890
|
+
required: ["action"],
|
|
3891
|
+
properties: {
|
|
3892
|
+
action: {
|
|
3893
|
+
type: "string",
|
|
3894
|
+
enum: ["diff", "rewind", "timeline", "decay"],
|
|
3895
|
+
description: "\uC218\uD589\uD560 \uC2DC\uACC4\uC5F4 \uB3D9\uC791 (diff: \uBE44\uAD50, rewind: \uB418\uB3CC\uB9AC\uAE30, timeline: \uD0C0\uC784\uB77C\uC778, decay: \uAC10\uC1E0 \uACC4\uC0B0)"
|
|
3896
|
+
},
|
|
3897
|
+
fromId: { type: "string", description: "\uBE44\uAD50 \uAE30\uC900 \uC2DC\uC791 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (diff \uC804\uC6A9)" },
|
|
3898
|
+
toId: { type: "string", description: "\uBE44\uAD50 \uB300\uC0C1 \uBAA9\uD45C \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (diff \uC804\uC6A9)" },
|
|
3899
|
+
targetId: { type: "string", description: "\uB9AC\uC640\uC778\uB4DC \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID (rewind \uC804\uC6A9)" },
|
|
3900
|
+
limit: { type: "number", description: "\uD0C0\uC784\uB77C\uC778 \uC870\uD68C \uAC1C\uC218" }
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
},
|
|
3904
|
+
{
|
|
3905
|
+
name: "scan_causal_graveyard",
|
|
3906
|
+
description: "\uBD80\uC815\uC801 \uAE30\uC5B5 / \uC548\uD2F0\uD328\uD134 \uD568\uC815(Graveyard)\uC744 \uC2E4\uC2DC\uAC04\uC73C\uB85C \uC2A4\uCE94\uD558\uC5EC \uC2E4\uD328\uB97C \uC720\uBC1C\uD588\uB358 \uC811\uADFC\uBC95\uC778\uC9C0 \uC120\uC81C \uAC80\uC99D\uD558\uAC70\uB098, \uC0C8\uB85C\uC6B4 \uC2E4\uD328 \uAC00\uC124\uC744 \uB4F1\uB85D\uD569\uB2C8\uB2E4.",
|
|
3907
|
+
inputSchema: {
|
|
3908
|
+
type: "object",
|
|
3909
|
+
required: ["action"],
|
|
3910
|
+
properties: {
|
|
3911
|
+
action: {
|
|
3912
|
+
type: "string",
|
|
3913
|
+
enum: ["scan", "assert", "list"],
|
|
3914
|
+
description: "\uB3D9\uC791 (scan: \uD568\uC815 \uAC80\uC0AC, assert: \uC2E4\uD328 \uAC00\uC124 \uB4F1\uB85D, list: \uC804\uCCB4 \uBAA9\uB85D)"
|
|
3915
|
+
},
|
|
3916
|
+
files: { type: "array", items: { type: "string" }, description: "\uC218\uC815 \uC608\uC815 \uD30C\uC77C \uBAA9\uB85D (scan\uC6A9)" },
|
|
3917
|
+
keywords: { type: "array", items: { type: "string" }, description: "\uC758\uB3C4/\uC811\uADFC\uBC95 \uD0A4\uC6CC\uB4DC (scan\uC6A9)" },
|
|
3918
|
+
trapPattern: { type: "string", description: "\uC2E4\uD328 \uC720\uBC1C \uD328\uD134 \uC774\uB984 (assert\uC6A9)" },
|
|
3919
|
+
domain: { type: "string", description: "\uB3C4\uBA54\uC778 (assert\uC6A9)" },
|
|
3920
|
+
reason: { type: "string", description: "\uC2E4\uD328 \uC6D0\uC778 (assert\uC6A9)" },
|
|
3921
|
+
safeAlternative: { type: "string", description: "\uC548\uC804\uD55C \uB300\uC548 (assert\uC6A9)" },
|
|
3922
|
+
severity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"], description: "\uC704\uD5D8\uB3C4" },
|
|
3923
|
+
avoidFiles: { type: "array", items: { type: "string" }, description: "\uAE08\uC9C0 \uD30C\uC77C \uD328\uD134" }
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
},
|
|
3927
|
+
{
|
|
3928
|
+
name: "branch_causal_context",
|
|
3929
|
+
description: "\uC791\uC5C5 \uB9E5\uB77D\uC744 \uB3C5\uB9BD\uB41C \uBE0C\uB79C\uCE58(Mental Fork)\uB85C \uBD84\uAE30\uD558\uAC70\uB098, \uAC80\uC99D \uD6C4 \uBA54\uC778\uC5D0 \uBCD1\uD569(Merge)\uD558\uACE0, \uC2E4\uD328\uD55C \uC2E4\uD5D8\uC740 \uD3D0\uAE30(Abandon)\uD558\uC5EC Graveyard\uC5D0 \uC548\uD2F0\uD328\uD134\uC73C\uB85C \uC790\uB3D9 \uC774\uAD00\uD569\uB2C8\uB2E4.",
|
|
3930
|
+
inputSchema: {
|
|
3931
|
+
type: "object",
|
|
3932
|
+
required: ["action"],
|
|
3933
|
+
properties: {
|
|
3934
|
+
action: {
|
|
3935
|
+
type: "string",
|
|
3936
|
+
enum: ["list", "create", "switch", "merge", "abandon"],
|
|
3937
|
+
description: "\uBE0C\uB79C\uCE58 \uB3D9\uC791 (list: \uBAA9\uB85D, create: \uC0DD\uC131, switch: \uC804\uD658, merge: \uBCD1\uD569, abandon: \uD3D0\uAE30 \uBC0F \uD568\uC815 \uB4F1\uB85D)"
|
|
3938
|
+
},
|
|
3939
|
+
name: { type: "string", description: "\uBE0C\uB79C\uCE58 \uC774\uB984" },
|
|
3940
|
+
fromBranch: { type: "string", description: "\uBD84\uAE30 \uAE30\uC900 \uBD80\uBAA8 \uBE0C\uB79C\uCE58 (create\uC6A9)" },
|
|
3941
|
+
targetBranch: { type: "string", description: "\uBCD1\uD569 \uB300\uC0C1 \uBE0C\uB79C\uCE58 (merge\uC6A9)" },
|
|
3942
|
+
failureReason: { type: "string", description: "\uD3D0\uAE30 \uC0AC\uC720 (abandon\uC6A9)" },
|
|
3943
|
+
safeAlternative: { type: "string", description: "\uC548\uC804\uD55C \uB300\uC548 (abandon\uC6A9)" }
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
},
|
|
3947
|
+
{
|
|
3948
|
+
name: "predict_blast_radius",
|
|
3949
|
+
description: "\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C\uB4E4\uC744 \uAE30\uBC18\uC73C\uB85C \uC778\uACFC \uADF8\uB798\uD504\uC758 2~3\uB2E8\uACC4 \uC5F0\uC1C4 \uD30C\uAE09 \uC601\uD5A5(Blast Radius)\uACFC \uC704\uD5D8\uB3C4(0~100)\uB97C \uC0B0\uCD9C\uD558\uACE0, \uC704\uBC18 \uBC29\uC9C0\uB97C \uC704\uD574 \uAD00\uB828 \uBD88\uBCC0\uC2DD\uB4E4\uC744 \uC0AC\uC804 \uD398\uCE58(prefetch)\uD569\uB2C8\uB2E4.",
|
|
3950
|
+
inputSchema: {
|
|
3951
|
+
type: "object",
|
|
3952
|
+
required: ["files"],
|
|
3953
|
+
properties: {
|
|
3954
|
+
files: {
|
|
1529
3955
|
type: "array",
|
|
1530
|
-
items: { type: "string" }
|
|
3956
|
+
items: { type: "string" },
|
|
3957
|
+
description: "\uC218\uC815 \uB300\uC0C1 \uD30C\uC77C \uBAA9\uB85D"
|
|
3958
|
+
},
|
|
3959
|
+
maxHops: {
|
|
3960
|
+
type: "number",
|
|
3961
|
+
description: "\uCD5C\uB300 \uD30C\uAE09 \uC804\uD30C \uB2E8\uACC4 (\uAE30\uBCF8\uAC12: 3)"
|
|
1531
3962
|
}
|
|
1532
3963
|
}
|
|
1533
3964
|
}
|
|
1534
3965
|
},
|
|
1535
3966
|
{
|
|
1536
|
-
name: "
|
|
1537
|
-
description: "
|
|
3967
|
+
name: "synthesize_git_checkpoint",
|
|
3968
|
+
description: "Git \uC0C1\uD0DC \uBC0F diff\uB97C \uBD84\uC11D\uD558\uC5EC \uC791\uC5C5\uC790\uC758 \uC758\uB3C4(Goal, Completed Steps, Pending Tasks)\uB97C \uC790\uB3D9 \uC5ED\uD569\uC131(Reverse Synthesis)\uD558\uACE0 \uC6D0\uD074\uB9AD \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4.",
|
|
1538
3969
|
inputSchema: {
|
|
1539
3970
|
type: "object",
|
|
1540
|
-
required: ["deletions"],
|
|
1541
3971
|
properties: {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
type: "object",
|
|
1546
|
-
required: ["entityName", "observations"],
|
|
1547
|
-
properties: {
|
|
1548
|
-
entityName: { type: "string" },
|
|
1549
|
-
observations: { type: "array", items: { type: "string" } }
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
3972
|
+
cwd: { type: "string", description: "\uC791\uC5C5 \uB514\uB809\uD130\uB9AC \uACBD\uB85C" },
|
|
3973
|
+
title: { type: "string", description: "\uC0AC\uC6A9\uC790 \uC9C0\uC815 \uC81C\uBAA9 (\uC0DD\uB7B5 \uC2DC \uC790\uB3D9 \uC0DD\uC131)" },
|
|
3974
|
+
autoCheckpoint: { type: "boolean", description: "\uBD84\uC11D \uD6C4 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC989\uC2DC \uC800\uC7A5 \uC5EC\uBD80 (\uAE30\uBCF8: true)" }
|
|
1553
3975
|
}
|
|
1554
3976
|
}
|
|
1555
3977
|
},
|
|
1556
3978
|
{
|
|
1557
|
-
name: "
|
|
1558
|
-
description: "
|
|
3979
|
+
name: "sync_obsidian_lens",
|
|
3980
|
+
description: "\uC635\uC2DC\uB514\uC5B8(Obsidian) \uBCFC\uD2B8\uC640 \uC778\uACFC\uC728 \uBA54\uBAA8\uB9AC(\uADDC\uCE59, \uD568\uC815, \uC2A4\uB0C5\uC0F7, \uD0C0\uC784\uB77C\uC778, \uCE94\uBC84\uC2A4 \uB2E4\uC774\uC5B4\uADF8\uB7A8)\uB97C \uC644\uBCBD\uD558\uAC8C \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654(Sync)\uD569\uB2C8\uB2E4.",
|
|
1559
3981
|
inputSchema: {
|
|
1560
3982
|
type: "object",
|
|
1561
|
-
required: ["relations"],
|
|
1562
3983
|
properties: {
|
|
1563
|
-
|
|
1564
|
-
type: "
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
relationType: { type: "string" }
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
3984
|
+
action: {
|
|
3985
|
+
type: "string",
|
|
3986
|
+
enum: ["export", "import", "sync"],
|
|
3987
|
+
description: "\uB3D9\uC791 (export: \uB9C8\uD06C\uB2E4\uC6B4/\uCE94\uBC84\uC2A4 \uB0B4\uBCF4\uB0B4\uAE30, import: \uB9C8\uD06C\uB2E4\uC6B4 \uC77D\uC5B4\uC624\uAE30, sync: \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654)"
|
|
3988
|
+
},
|
|
3989
|
+
vaultPath: {
|
|
3990
|
+
type: "string",
|
|
3991
|
+
description: "\uC635\uC2DC\uB514\uC5B8 \uBCFC\uD2B8 \uB8E8\uD2B8 \uACBD\uB85C (\uC0DD\uB7B5 \uC2DC \uAE30\uBCF8 \uD658\uACBD\uBCC0\uC218/OS \uACBD\uB85C \uC0AC\uC6A9)"
|
|
1574
3992
|
}
|
|
1575
3993
|
}
|
|
1576
3994
|
}
|
|
@@ -1954,13 +4372,13 @@ init_causal_server();
|
|
|
1954
4372
|
// causal-migrator.ts
|
|
1955
4373
|
init_causal_config();
|
|
1956
4374
|
init_causal_store();
|
|
1957
|
-
import { existsSync as
|
|
1958
|
-
import { dirname as
|
|
4375
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
|
|
4376
|
+
import { dirname as dirname5 } from "path";
|
|
1959
4377
|
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 (!
|
|
4378
|
+
if (!existsSync7(sourceBackupPath)) {
|
|
1961
4379
|
throw new Error(`\uAD6C\uD615 \uBA54\uBAA8\uB9AC \uBC31\uC5C5 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${sourceBackupPath}`);
|
|
1962
4380
|
}
|
|
1963
|
-
const raw =
|
|
4381
|
+
const raw = readFileSync6(sourceBackupPath, "utf-8");
|
|
1964
4382
|
const legacy = JSON.parse(raw);
|
|
1965
4383
|
const entities = legacy.entities || [];
|
|
1966
4384
|
const legacyRelations = legacy.relations || [];
|
|
@@ -2074,11 +4492,11 @@ function migrateLegacyMemoryGraph(sourceBackupPath = CAUSAL_MEMORY_CONFIG.LEGACY
|
|
|
2074
4492
|
};
|
|
2075
4493
|
const serialized = JSON.stringify(db, null, 2);
|
|
2076
4494
|
for (const outPath of [targetGlobalPath, targetLocalPath]) {
|
|
2077
|
-
const dir =
|
|
2078
|
-
if (!
|
|
2079
|
-
|
|
4495
|
+
const dir = dirname5(outPath);
|
|
4496
|
+
if (!existsSync7(dir)) {
|
|
4497
|
+
mkdirSync6(dir, { recursive: true });
|
|
2080
4498
|
}
|
|
2081
|
-
|
|
4499
|
+
writeFileSync6(outPath, serialized, "utf-8");
|
|
2082
4500
|
}
|
|
2083
4501
|
const alwaysActiveCount = rules.filter((r) => r.triggers.alwaysActive).length;
|
|
2084
4502
|
const domains = Array.from(new Set(rules.map((r) => r.domain)));
|
|
@@ -2263,6 +4681,13 @@ async function syncWithPeer(remoteHost, port = CAUSAL_MEMORY_CONFIG.DEFAULT_PORT
|
|
|
2263
4681
|
}
|
|
2264
4682
|
|
|
2265
4683
|
// cli.ts
|
|
4684
|
+
init_causal_context();
|
|
4685
|
+
init_causal_timeseries();
|
|
4686
|
+
init_causal_graveyard();
|
|
4687
|
+
init_causal_branch();
|
|
4688
|
+
init_causal_predictor();
|
|
4689
|
+
init_causal_synthesizer();
|
|
4690
|
+
init_causal_lens();
|
|
2266
4691
|
var RESET2 = "\x1B[0m";
|
|
2267
4692
|
var BOLD2 = "\x1B[1m";
|
|
2268
4693
|
var CYAN2 = "\x1B[36m";
|
|
@@ -2279,6 +4704,18 @@ ${BOLD2}\uC0AC\uC6A9\uBC95:${RESET2}
|
|
|
2279
4704
|
|
|
2280
4705
|
${BOLD2}\uC11C\uBE0C \uBA85\uB839\uC5B4:${RESET2}
|
|
2281
4706
|
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)
|
|
4707
|
+
checkpoint \uD604\uC7AC \uC791\uC5C5 \uC138\uC158\uC758 \uD575\uC2EC \uCEE8\uD14D\uC2A4\uD2B8(\uBAA9\uD45C, \uC644\uB8CC, \uD560\uC77C, \uC544\uD2F0\uD329\uD2B8)\uB97C \uC2A4\uB0C5\uC0F7\uC73C\uB85C \uC601\uAD6C \uBC15\uC81C
|
|
4708
|
+
resume \uC800\uC7A5\uB41C \uCD5C\uC2E0(\uB610\uB294 \uD2B9\uC815 ID) \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7\uC744 0ms \uB9CC\uC5D0 \uBCF5\uC6D0\uD558\uC5EC \uC0C8 \uCC44\uD305\uC5D0 \uC8FC\uC785
|
|
4709
|
+
checkpoints \uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uB0C5\uC0F7(Checkpoint) \uBAA9\uB85D \uC870\uD68C \uBC0F \uAD00\uB9AC (--delete <id>)
|
|
4710
|
+
timeline \uC138\uC158 \uC9C4\uD654 \uD0C0\uC784\uB77C\uC778 \uBC0F \uC2DC\uACC4\uC5F4 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC774\uB825 \uC870\uD68C (--limit 20)
|
|
4711
|
+
diff \uB450 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uAC04 \uBCC0\uACBD \uC0AC\uD56D(\uC2E0\uADDC \uC644\uB8CC \uC791\uC5C5, \uC794\uC5EC \uACFC\uC81C, \uD30C\uC77C) \uC815\uBC00 \uBE44\uAD50
|
|
4712
|
+
rewind \uACFC\uAC70 \uD2B9\uC815 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC2DC\uC810\uC73C\uB85C \uCEE8\uD14D\uC2A4\uD2B8 \uC989\uC2DC \uB418\uAC10\uAE30
|
|
4713
|
+
graveyard \uC2E4\uD328\uD588\uB358 \uC548\uD2F0\uD328\uD134 \uD568\uC815 \uBAA9\uB85D \uC870\uD68C \uBC0F \uC2E0\uADDC \uC2E4\uD328 \uAC00\uC124 \uB4F1\uB85D
|
|
4714
|
+
trap-check \uC218\uC815 \uC608\uC815 \uD30C\uC77C/\uD0A4\uC6CC\uB4DC\uAC00 \uACFC\uAC70 \uC2E4\uD328 \uD328\uD134\uC5D0 \uD574\uB2F9\uD558\uB294\uC9C0 \uC120\uC81C \uAC80\uC0AC
|
|
4715
|
+
branch \uC791\uC5C5 \uB9E5\uB77D \uB3C5\uB9BD \uBD84\uAE30(Fork), \uC804\uD658(Switch), \uBCD1\uD569(Merge), \uD3D0\uAE30(Abandon) \uAD00\uB9AC
|
|
4716
|
+
predict \uC218\uC815 \uB300\uC0C1 \uD30C\uC77C \uAE30\uBC18 \uB2E4\uB2E8\uACC4 \uC778\uACFC \uC5F0\uC1C4 \uD30C\uAE09 \uBC18\uACBD(Blast Radius) \uBD84\uC11D
|
|
4717
|
+
auto-checkpoint Git diff/\uC0C1\uD0DC\uB97C \uBD84\uC11D\uD558\uC5EC \uC758\uB3C4(Goal, Tasks) \uC790\uB3D9 \uC5ED\uD569\uC131 \uBC0F \uC6D0\uD074\uB9AD \uBC15\uC81C
|
|
4718
|
+
lens \uC635\uC2DC\uB514\uC5B8(Obsidian) \uBCFC\uD2B8\uC640 \uC778\uACFC\uC728 \uC9C0\uC2DD/\uC2A4\uB0C5\uC0F7/\uCE94\uBC84\uC2A4 \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 (export|import|sync)
|
|
2282
4719
|
list \uB4F1\uB85D\uB41C \uB3C4\uBA54\uC778, \uC778\uACFC \uADDC\uCE59 \uBC0F \uC778\uACFC \uAC04\uC120(Relations) \uD1B5\uACC4 \uD655\uC778
|
|
2283
4720
|
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
4721
|
assert \uC2E0\uADDC \uC778\uACFC\uC728 \uADDC\uCE59 \uB4F1\uB85D \uB610\uB294 \uAC31\uC2E0 (--json '<json>')
|
|
@@ -2725,6 +5162,487 @@ ${BOLD2}${GREEN2}\uD83D\uDE80 [Causal Memory Daemon \uAC00\uB3D9 \uC644\uB8CC]${
|
|
|
2725
5162
|
await new Promise(() => {});
|
|
2726
5163
|
return 0;
|
|
2727
5164
|
}
|
|
5165
|
+
if (subCommand === "checkpoint" || subCommand === "save-context") {
|
|
5166
|
+
const contextStore = new ContextCheckpointStore;
|
|
5167
|
+
const jsonIdx = subArgs.indexOf("--json");
|
|
5168
|
+
let input;
|
|
5169
|
+
if (jsonIdx >= 0 && subArgs[jsonIdx + 1]) {
|
|
5170
|
+
input = JSON.parse(subArgs[jsonIdx + 1]);
|
|
5171
|
+
} else {
|
|
5172
|
+
let title = "";
|
|
5173
|
+
let sessionId = "default-session";
|
|
5174
|
+
let goal = "";
|
|
5175
|
+
const completedSteps = [];
|
|
5176
|
+
const pendingTasks = [];
|
|
5177
|
+
const keyDecisions = [];
|
|
5178
|
+
const activeFiles = [];
|
|
5179
|
+
const artifacts = [];
|
|
5180
|
+
const positional = subArgs.filter((a) => a !== "checkpoint" && a !== "save-context" && !a.startsWith("-"));
|
|
5181
|
+
if (positional.length > 0) {
|
|
5182
|
+
title = positional[0];
|
|
5183
|
+
}
|
|
5184
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5185
|
+
const arg = subArgs[i];
|
|
5186
|
+
if (arg === "--title" && subArgs[i + 1]) {
|
|
5187
|
+
title = subArgs[++i];
|
|
5188
|
+
} else if (arg === "--session" && subArgs[i + 1]) {
|
|
5189
|
+
sessionId = subArgs[++i];
|
|
5190
|
+
} else if (arg === "--goal" && subArgs[i + 1]) {
|
|
5191
|
+
goal = subArgs[++i];
|
|
5192
|
+
} else if (arg === "--completed" && subArgs[i + 1]) {
|
|
5193
|
+
completedSteps.push(subArgs[++i]);
|
|
5194
|
+
} else if (arg === "--pending" && subArgs[i + 1]) {
|
|
5195
|
+
pendingTasks.push(subArgs[++i]);
|
|
5196
|
+
} else if (arg === "--decision" && subArgs[i + 1]) {
|
|
5197
|
+
keyDecisions.push(subArgs[++i]);
|
|
5198
|
+
} else if (arg === "--file" && subArgs[i + 1]) {
|
|
5199
|
+
activeFiles.push(subArgs[++i]);
|
|
5200
|
+
} else if (arg === "--artifact" && subArgs[i + 1]) {
|
|
5201
|
+
artifacts.push(subArgs[++i]);
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
if (!title) {
|
|
5205
|
+
title = `Context Snapshot (${new Date().toISOString().slice(0, 16).replace("T", " ")})`;
|
|
5206
|
+
}
|
|
5207
|
+
input = {
|
|
5208
|
+
title,
|
|
5209
|
+
sessionId,
|
|
5210
|
+
goal: goal || title,
|
|
5211
|
+
completedSteps: completedSteps.length > 0 ? completedSteps : undefined,
|
|
5212
|
+
pendingTasks: pendingTasks.length > 0 ? pendingTasks : undefined,
|
|
5213
|
+
keyDecisions: keyDecisions.length > 0 ? keyDecisions : undefined,
|
|
5214
|
+
activeFiles: activeFiles.length > 0 ? activeFiles : undefined,
|
|
5215
|
+
artifacts: artifacts.length > 0 ? artifacts : undefined
|
|
5216
|
+
};
|
|
5217
|
+
}
|
|
5218
|
+
const saved = await contextStore.saveCheckpoint(input);
|
|
5219
|
+
if (options?.agentMode || options?.compact) {
|
|
5220
|
+
process.stdout.write(JSON.stringify({ ok: true, checkpoint: saved }) + `
|
|
5221
|
+
`);
|
|
5222
|
+
} else {
|
|
5223
|
+
process.stdout.write(`
|
|
5224
|
+
${BOLD2}${GREEN2}\u2728 [Context Checkpoint \uBC15\uC81C \uC644\uB8CC]${RESET2}
|
|
5225
|
+
` + ` \u2022 ID : ${BOLD2}${CYAN2}${saved.id}${RESET2}
|
|
5226
|
+
` + ` \u2022 \uC138\uC158 : ${saved.sessionId}
|
|
5227
|
+
` + ` \u2022 \uC81C\uBAA9 : ${BOLD2}${saved.title}${RESET2}
|
|
5228
|
+
` + ` \u2022 \uBAA9\uD45C(Goal) : ${saved.goal}
|
|
5229
|
+
` + ` \u2022 \uBCC0\uACBD \uD30C\uC77C : ${saved.modifiedFiles.length}\uAC1C \uAC10\uC9C0
|
|
5230
|
+
` + ` \u2022 \uB2E4\uC74C \uD560 \uC77C : ${saved.pendingTasks.length}\uAC74 \uBCF4\uC874
|
|
5231
|
+
` + ` \u2022 \uBCF5\uC6D0 \uBA85\uB839\uC5B4 : ${YELLOW2}synapse memory resume ${saved.id}${RESET2}
|
|
5232
|
+
|
|
5233
|
+
`);
|
|
5234
|
+
}
|
|
5235
|
+
return 0;
|
|
5236
|
+
}
|
|
5237
|
+
if (subCommand === "resume" || subCommand === "load-context" || subCommand === "restore") {
|
|
5238
|
+
const contextStore = new ContextCheckpointStore;
|
|
5239
|
+
let targetId = "latest";
|
|
5240
|
+
let sessionId;
|
|
5241
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5242
|
+
const arg = subArgs[i];
|
|
5243
|
+
if (arg === "--session" && subArgs[i + 1]) {
|
|
5244
|
+
sessionId = subArgs[++i];
|
|
5245
|
+
} else if (arg === "--id" && subArgs[i + 1]) {
|
|
5246
|
+
targetId = subArgs[++i];
|
|
5247
|
+
} else if (arg !== "resume" && arg !== "load-context" && arg !== "restore" && !arg.startsWith("-")) {
|
|
5248
|
+
targetId = arg;
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
5251
|
+
const checkpoint = contextStore.getCheckpoint(targetId, sessionId);
|
|
5252
|
+
if (!checkpoint) {
|
|
5253
|
+
if (options?.agentMode || options?.compact) {
|
|
5254
|
+
process.stdout.write(JSON.stringify({ ok: false, error: "\uC800\uC7A5\uB41C \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." }) + `
|
|
5255
|
+
`);
|
|
5256
|
+
} else {
|
|
5257
|
+
process.stderr.write(`${YELLOW2}\u26A0\uFE0F \uBCF5\uC6D0\uD560 \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${RESET2}
|
|
5258
|
+
`);
|
|
5259
|
+
}
|
|
5260
|
+
return 1;
|
|
5261
|
+
}
|
|
5262
|
+
if (options?.compact) {
|
|
5263
|
+
process.stdout.write(ContextCheckpointStore.formatForAgentResume(checkpoint, true) + `
|
|
5264
|
+
`);
|
|
5265
|
+
} else if (options?.agentMode && subArgs.includes("--json")) {
|
|
5266
|
+
process.stdout.write(JSON.stringify({ ok: true, checkpoint }, null, 2) + `
|
|
5267
|
+
`);
|
|
5268
|
+
} else {
|
|
5269
|
+
process.stdout.write(`
|
|
5270
|
+
` + ContextCheckpointStore.formatForAgentResume(checkpoint, false) + `
|
|
5271
|
+
|
|
5272
|
+
`);
|
|
5273
|
+
}
|
|
5274
|
+
return 0;
|
|
5275
|
+
}
|
|
5276
|
+
if (subCommand === "checkpoints" || subCommand === "list-checkpoints") {
|
|
5277
|
+
const contextStore = new ContextCheckpointStore;
|
|
5278
|
+
let sessionId;
|
|
5279
|
+
let limit = 20;
|
|
5280
|
+
let deleteId;
|
|
5281
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5282
|
+
if (subArgs[i] === "--session" && subArgs[i + 1]) {
|
|
5283
|
+
sessionId = subArgs[++i];
|
|
5284
|
+
} else if (subArgs[i] === "--limit" && subArgs[i + 1]) {
|
|
5285
|
+
limit = parseInt(subArgs[++i], 10);
|
|
5286
|
+
} else if (subArgs[i] === "--delete" && subArgs[i + 1]) {
|
|
5287
|
+
deleteId = subArgs[++i];
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
if (deleteId) {
|
|
5291
|
+
const deleted = contextStore.deleteCheckpoint(deleteId);
|
|
5292
|
+
if (options?.agentMode) {
|
|
5293
|
+
process.stdout.write(JSON.stringify({ ok: deleted, deletedId: deleteId }) + `
|
|
5294
|
+
`);
|
|
5295
|
+
} else if (deleted) {
|
|
5296
|
+
process.stdout.write(`${GREEN2}\u2705 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uC0AD\uC81C \uC644\uB8CC: ${deleteId}${RESET2}
|
|
5297
|
+
`);
|
|
5298
|
+
} else {
|
|
5299
|
+
process.stderr.write(`${RED2}\u274C \uD574\uB2F9 ID\uC758 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${deleteId}${RESET2}
|
|
5300
|
+
`);
|
|
5301
|
+
}
|
|
5302
|
+
return deleted ? 0 : 1;
|
|
5303
|
+
}
|
|
5304
|
+
const list = contextStore.listCheckpoints({ sessionId, limit });
|
|
5305
|
+
if (options?.agentMode || options?.compact) {
|
|
5306
|
+
process.stdout.write(JSON.stringify({ ok: true, total: list.length, checkpoints: list }) + `
|
|
5307
|
+
`);
|
|
5308
|
+
return 0;
|
|
5309
|
+
}
|
|
5310
|
+
process.stdout.write(`
|
|
5311
|
+
${BOLD2}${CYAN2}\uD83D\uDCCC [Causal Memory] \uCEE8\uD14D\uC2A4\uD2B8 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uBAA9\uB85D (${list.length}\uAC1C)${RESET2}
|
|
5312
|
+
|
|
5313
|
+
`);
|
|
5314
|
+
if (list.length === 0) {
|
|
5315
|
+
process.stdout.write(` \uC800\uC7A5\uB41C \uCCB4\uD06C\uD3EC\uC778\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. 'synapse memory checkpoint "<\uC81C\uBAA9>"'\uC73C\uB85C \uC0DD\uC131\uD558\uC138\uC694.
|
|
5316
|
+
|
|
5317
|
+
`);
|
|
5318
|
+
return 0;
|
|
5319
|
+
}
|
|
5320
|
+
for (const c of list) {
|
|
5321
|
+
process.stdout.write(` \u2022 ${BOLD2}${GREEN2}${c.id}${RESET2} ${CYAN2}[${c.sessionId}]${RESET2} ${BOLD2}${c.title}${RESET2}
|
|
5322
|
+
${GRAY2}\uC0DD\uC131: ${c.createdAt} | \uC644\uB8CC: ${c.completedSteps.length}\uAC74 | \uB2E4\uC74C \uD560\uC77C: ${c.pendingTasks.length}\uAC74${RESET2}
|
|
5323
|
+
`);
|
|
5324
|
+
}
|
|
5325
|
+
process.stdout.write(`
|
|
5326
|
+
`);
|
|
5327
|
+
return 0;
|
|
5328
|
+
}
|
|
5329
|
+
if (subCommand === "timeline") {
|
|
5330
|
+
const contextStore = new ContextCheckpointStore;
|
|
5331
|
+
const timeSeries = new CausalTimeSeriesEngine(contextStore);
|
|
5332
|
+
let limit = 20;
|
|
5333
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5334
|
+
if (subArgs[i] === "--limit" && subArgs[i + 1]) {
|
|
5335
|
+
limit = parseInt(subArgs[++i], 10);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
const tl = timeSeries.generateTimeline(limit);
|
|
5339
|
+
if (options?.agentMode || options?.compact) {
|
|
5340
|
+
process.stdout.write(JSON.stringify(tl) + `
|
|
5341
|
+
`);
|
|
5342
|
+
} else {
|
|
5343
|
+
process.stdout.write(`
|
|
5344
|
+
${BOLD2}${CYAN2}\u23F3 [Causal Timeline] \uC138\uC158 \uC9C4\uD654 \uD0C0\uC784\uB77C\uC778 (\uCD1D ${tl.length}\uAC1C)${RESET2}
|
|
5345
|
+
|
|
5346
|
+
`);
|
|
5347
|
+
for (const ev of tl) {
|
|
5348
|
+
process.stdout.write(` \u2022 ${BOLD2}${GREEN2}${ev.checkpointId || ev.id}${RESET2} (${ev.timestamp}) - ${BOLD2}${ev.title}${RESET2}
|
|
5349
|
+
${GRAY2}${ev.summary}${RESET2}
|
|
5350
|
+
`);
|
|
5351
|
+
}
|
|
5352
|
+
process.stdout.write(`
|
|
5353
|
+
`);
|
|
5354
|
+
}
|
|
5355
|
+
return 0;
|
|
5356
|
+
}
|
|
5357
|
+
if (subCommand === "diff") {
|
|
5358
|
+
const positional = subArgs.filter((a) => a !== "diff" && !a.startsWith("-"));
|
|
5359
|
+
const fromId = positional[0];
|
|
5360
|
+
const toId = positional[1];
|
|
5361
|
+
if (!fromId || !toId) {
|
|
5362
|
+
process.stderr.write(`${RED2}\u274C \uB450 \uAC1C\uC758 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. (\uC608: synapse memory diff <fromId> <toId>)${RESET2}
|
|
5363
|
+
`);
|
|
5364
|
+
return 1;
|
|
5365
|
+
}
|
|
5366
|
+
const timeSeries = new CausalTimeSeriesEngine;
|
|
5367
|
+
const diff = timeSeries.diffCheckpoints(fromId, toId);
|
|
5368
|
+
if (!diff) {
|
|
5369
|
+
process.stderr.write(`${RED2}\u274C \uBE44\uAD50 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${RESET2}
|
|
5370
|
+
`);
|
|
5371
|
+
return 1;
|
|
5372
|
+
}
|
|
5373
|
+
if (options?.agentMode || options?.compact) {
|
|
5374
|
+
process.stdout.write(JSON.stringify(diff, null, 2) + `
|
|
5375
|
+
`);
|
|
5376
|
+
} else {
|
|
5377
|
+
process.stdout.write(`
|
|
5378
|
+
${BOLD2}${CYAN2}\uD83D\uDCCA [Causal Diff] ${diff.fromCheckpointId} \u2794 ${diff.toCheckpointId}${RESET2}
|
|
5379
|
+
|
|
5380
|
+
` + ` \u2022 \uC2E0\uADDC \uC644\uB8CC \uB2E8\uACC4 : ${diff.newCompletedSteps.length}\uAC74
|
|
5381
|
+
`);
|
|
5382
|
+
for (const s of diff.newCompletedSteps) {
|
|
5383
|
+
process.stdout.write(` + ${s}
|
|
5384
|
+
`);
|
|
5385
|
+
}
|
|
5386
|
+
process.stdout.write(` \u2022 \uD574\uACB0\uB41C \uC794\uC5EC\uACFC\uC81C : ${diff.resolvedTasks.length}\uAC74
|
|
5387
|
+
` + ` \u2022 \uC0C8\uB85C \uC218\uC815\uB41C \uD30C\uC77C : ${diff.modifiedFilesDelta.added.length}\uAC1C
|
|
5388
|
+
|
|
5389
|
+
`);
|
|
5390
|
+
}
|
|
5391
|
+
return 0;
|
|
5392
|
+
}
|
|
5393
|
+
if (subCommand === "rewind") {
|
|
5394
|
+
const positional = subArgs.filter((a) => a !== "rewind" && !a.startsWith("-"));
|
|
5395
|
+
const targetId = positional[0];
|
|
5396
|
+
if (!targetId) {
|
|
5397
|
+
process.stderr.write(`${RED2}\u274C \uB418\uAC10\uAE30\uD560 \uCCB4\uD06C\uD3EC\uC778\uD2B8 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. (\uC608: synapse memory rewind <id>)${RESET2}
|
|
5398
|
+
`);
|
|
5399
|
+
return 1;
|
|
5400
|
+
}
|
|
5401
|
+
const timeSeries = new CausalTimeSeriesEngine;
|
|
5402
|
+
const res = await timeSeries.rewindToCheckpoint(targetId);
|
|
5403
|
+
if (options?.agentMode || options?.compact) {
|
|
5404
|
+
process.stdout.write(JSON.stringify(res) + `
|
|
5405
|
+
`);
|
|
5406
|
+
} else if (res && res.id) {
|
|
5407
|
+
process.stdout.write(`
|
|
5408
|
+
${BOLD2}${GREEN2}\u23EA [Causal Rewind \uC131\uACF5] ${targetId} \uC2DC\uC810\uC73C\uB85C \uBCF5\uC6D0\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${res.id}${RESET2}
|
|
5409
|
+
|
|
5410
|
+
`);
|
|
5411
|
+
} else {
|
|
5412
|
+
process.stderr.write(`
|
|
5413
|
+
${BOLD2}${RED2}\uD83D\uDEA8 [Causal Rewind \uC2E4\uD328] \uBCF5\uC6D0 \uB300\uC0C1 \uCCB4\uD06C\uD3EC\uC778\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${RESET2}
|
|
5414
|
+
|
|
5415
|
+
`);
|
|
5416
|
+
}
|
|
5417
|
+
return res && res.id ? 0 : 1;
|
|
5418
|
+
}
|
|
5419
|
+
if (subCommand === "graveyard") {
|
|
5420
|
+
const graveyard = new CausalGraveyardStore;
|
|
5421
|
+
const jsonIdx = subArgs.indexOf("--assert");
|
|
5422
|
+
if (jsonIdx >= 0 && subArgs[jsonIdx + 1]) {
|
|
5423
|
+
const trapInput = JSON.parse(subArgs[jsonIdx + 1]);
|
|
5424
|
+
const trap = graveyard.assertTrap(trapInput);
|
|
5425
|
+
process.stdout.write(`${GREEN2}\u2705 \uC2E4\uD328 \uC548\uD2F0\uD328\uD134 \uD568\uC815 \uB4F1\uB85D \uC644\uB8CC: ${trap.id}${RESET2}
|
|
5426
|
+
`);
|
|
5427
|
+
return 0;
|
|
5428
|
+
}
|
|
5429
|
+
const traps = graveyard.getAllTraps();
|
|
5430
|
+
if (options?.agentMode || options?.compact) {
|
|
5431
|
+
process.stdout.write(JSON.stringify({ ok: true, total: traps.length, traps }) + `
|
|
5432
|
+
`);
|
|
5433
|
+
} else {
|
|
5434
|
+
process.stdout.write(`
|
|
5435
|
+
${BOLD2}${RED2}\uD83E\uDEA6 [Causal Graveyard] \uC2E4\uD328 \uBC0F \uC548\uD2F0\uD328\uD134 \uD568\uC815 \uBCF4\uAD00\uC18C (${traps.length}\uAC1C)${RESET2}
|
|
5436
|
+
|
|
5437
|
+
`);
|
|
5438
|
+
for (const t of traps) {
|
|
5439
|
+
process.stdout.write(` \u2022 [${t.severity}] ${BOLD2}${t.trapPattern}${RESET2} (${t.domain})
|
|
5440
|
+
\uC6D0\uC778: ${t.reason}
|
|
5441
|
+
\uB300\uC548: ${t.safeAlternative}
|
|
5442
|
+
`);
|
|
5443
|
+
}
|
|
5444
|
+
process.stdout.write(`
|
|
5445
|
+
`);
|
|
5446
|
+
}
|
|
5447
|
+
return 0;
|
|
5448
|
+
}
|
|
5449
|
+
if (subCommand === "trap-check") {
|
|
5450
|
+
const files = [];
|
|
5451
|
+
const keywords = [];
|
|
5452
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5453
|
+
if (subArgs[i] === "--files" || subArgs[i] === "-f") {
|
|
5454
|
+
let j = i + 1;
|
|
5455
|
+
while (j < subArgs.length && !subArgs[j].startsWith("-")) {
|
|
5456
|
+
files.push(subArgs[j++]);
|
|
5457
|
+
}
|
|
5458
|
+
i = j - 1;
|
|
5459
|
+
} else if (subArgs[i] === "--keywords" || subArgs[i] === "-k") {
|
|
5460
|
+
let j = i + 1;
|
|
5461
|
+
while (j < subArgs.length && !subArgs[j].startsWith("-")) {
|
|
5462
|
+
keywords.push(subArgs[j++]);
|
|
5463
|
+
}
|
|
5464
|
+
i = j - 1;
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
const graveyard = new CausalGraveyardStore;
|
|
5468
|
+
const scan = graveyard.scanTraps({
|
|
5469
|
+
files: files.length > 0 ? files : undefined,
|
|
5470
|
+
keywords: keywords.length > 0 ? keywords : undefined
|
|
5471
|
+
});
|
|
5472
|
+
if (options?.agentMode || options?.compact) {
|
|
5473
|
+
process.stdout.write(JSON.stringify(scan, null, 2) + `
|
|
5474
|
+
`);
|
|
5475
|
+
} else {
|
|
5476
|
+
process.stdout.write(`
|
|
5477
|
+
` + CausalGraveyardStore.formatTrapWarnings(scan) + `
|
|
5478
|
+
|
|
5479
|
+
`);
|
|
5480
|
+
}
|
|
5481
|
+
return scan.trapped ? 1 : 0;
|
|
5482
|
+
}
|
|
5483
|
+
if (subCommand === "branch") {
|
|
5484
|
+
const branchAction = subArgs.find((a) => a !== "branch" && !a.startsWith("-")) || "list";
|
|
5485
|
+
const branchStore = new CausalBranchStore;
|
|
5486
|
+
if (branchAction === "list") {
|
|
5487
|
+
const branches = branchStore.listBranches();
|
|
5488
|
+
const current = branchStore.getCurrentBranch();
|
|
5489
|
+
if (options?.agentMode || options?.compact) {
|
|
5490
|
+
process.stdout.write(JSON.stringify({ ok: true, current, branches }) + `
|
|
5491
|
+
`);
|
|
5492
|
+
} else {
|
|
5493
|
+
process.stdout.write(`
|
|
5494
|
+
${BOLD2}${CYAN2}\uD83C\uDF3F [Causal Branch] \uC778\uACFC \uB9E5\uB77D \uBE0C\uB79C\uCE58 \uBAA9\uB85D${RESET2}
|
|
5495
|
+
|
|
5496
|
+
`);
|
|
5497
|
+
for (const b of branches) {
|
|
5498
|
+
const prefix = b.name === current.name ? `${BOLD2}${GREEN2}* ${b.name}${RESET2}` : ` ${b.name}`;
|
|
5499
|
+
process.stdout.write(`${prefix} (${b.status}) - ${b.description || "\uC124\uBA85 \uC5C6\uC74C"}
|
|
5500
|
+
`);
|
|
5501
|
+
}
|
|
5502
|
+
process.stdout.write(`
|
|
5503
|
+
`);
|
|
5504
|
+
}
|
|
5505
|
+
return 0;
|
|
5506
|
+
}
|
|
5507
|
+
if (branchAction === "create") {
|
|
5508
|
+
const name = subArgs.filter((a) => a !== "branch" && a !== "create" && !a.startsWith("-"))[0];
|
|
5509
|
+
if (!name) {
|
|
5510
|
+
process.stderr.write(`${RED2}\u274C \uBE0C\uB79C\uCE58 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. (\uC608: synapse memory branch create feat/test)${RESET2}
|
|
5511
|
+
`);
|
|
5512
|
+
return 1;
|
|
5513
|
+
}
|
|
5514
|
+
const b = await branchStore.createBranch(name);
|
|
5515
|
+
process.stdout.write(`${GREEN2}\u2705 \uBE0C\uB79C\uCE58 \uC0DD\uC131 \uC644\uB8CC: ${b.name}${RESET2}
|
|
5516
|
+
`);
|
|
5517
|
+
return 0;
|
|
5518
|
+
}
|
|
5519
|
+
if (branchAction === "switch") {
|
|
5520
|
+
const name = subArgs.filter((a) => a !== "branch" && a !== "switch" && !a.startsWith("-"))[0];
|
|
5521
|
+
if (!name) {
|
|
5522
|
+
process.stderr.write(`${RED2}\u274C \uC804\uD658\uD560 \uBE0C\uB79C\uCE58 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.${RESET2}
|
|
5523
|
+
`);
|
|
5524
|
+
return 1;
|
|
5525
|
+
}
|
|
5526
|
+
const b = branchStore.switchBranch(name);
|
|
5527
|
+
process.stdout.write(`${GREEN2}\u2705 \uBE0C\uB79C\uCE58 \uC804\uD658 \uC644\uB8CC: ${b.name}${RESET2}
|
|
5528
|
+
`);
|
|
5529
|
+
return 0;
|
|
5530
|
+
}
|
|
5531
|
+
if (branchAction === "merge") {
|
|
5532
|
+
const name = subArgs.filter((a) => a !== "branch" && a !== "merge" && !a.startsWith("-"))[0];
|
|
5533
|
+
if (!name) {
|
|
5534
|
+
process.stderr.write(`${RED2}\u274C \uBCD1\uD569\uD560 \uBE0C\uB79C\uCE58 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.${RESET2}
|
|
5535
|
+
`);
|
|
5536
|
+
return 1;
|
|
5537
|
+
}
|
|
5538
|
+
const res = await branchStore.mergeBranch(name);
|
|
5539
|
+
if (res && res.resultingCheckpoint) {
|
|
5540
|
+
process.stdout.write(`${GREEN2}\u2705 \uBE0C\uB79C\uCE58 \uBCD1\uD569 \uC131\uACF5: ${name}${RESET2}
|
|
5541
|
+
`);
|
|
5542
|
+
} else {
|
|
5543
|
+
process.stderr.write(`${RED2}\u274C \uBE0C\uB79C\uCE58 \uBCD1\uD569 \uC2E4\uD328: ${name}${RESET2}
|
|
5544
|
+
`);
|
|
5545
|
+
}
|
|
5546
|
+
return res ? 0 : 1;
|
|
5547
|
+
}
|
|
5548
|
+
if (branchAction === "abandon") {
|
|
5549
|
+
const name = subArgs.filter((a) => a !== "branch" && a !== "abandon" && !a.startsWith("-"))[0];
|
|
5550
|
+
if (!name) {
|
|
5551
|
+
process.stderr.write(`${RED2}\u274C \uD3D0\uAE30\uD560 \uBE0C\uB79C\uCE58 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.${RESET2}
|
|
5552
|
+
`);
|
|
5553
|
+
return 1;
|
|
5554
|
+
}
|
|
5555
|
+
const res = branchStore.abandonBranch(name, "\uC0AC\uC6A9\uC790 \uBA85\uB839\uC73C\uB85C \uD3D0\uAE30\uB428");
|
|
5556
|
+
process.stdout.write(`${YELLOW2}\u26A0\uFE0F \uBE0C\uB79C\uCE58 \uD3D0\uAE30 \uBC0F Graveyard \uD568\uC815 \uC774\uAD00 \uC644\uB8CC: ${name}${RESET2}
|
|
5557
|
+
`);
|
|
5558
|
+
return res.branch ? 0 : 1;
|
|
5559
|
+
}
|
|
5560
|
+
process.stderr.write(`${RED2}\u274C \uC54C \uC218 \uC5C6\uB294 branch \uC11C\uBE0C\uCEE4\uB9E8\uB4DC: ${branchAction}${RESET2}
|
|
5561
|
+
`);
|
|
5562
|
+
return 1;
|
|
5563
|
+
}
|
|
5564
|
+
if (subCommand === "predict") {
|
|
5565
|
+
const files = [];
|
|
5566
|
+
let maxHops = 3;
|
|
5567
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5568
|
+
if (subArgs[i] === "--hops" && subArgs[i + 1]) {
|
|
5569
|
+
maxHops = parseInt(subArgs[++i], 10);
|
|
5570
|
+
} else if (subArgs[i] !== "predict" && !subArgs[i].startsWith("-")) {
|
|
5571
|
+
files.push(subArgs[i]);
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
const predictor = new CausalPredictorEngine;
|
|
5575
|
+
const report = predictor.analyzeBlastRadius(files, maxHops);
|
|
5576
|
+
if (options?.agentMode || options?.compact) {
|
|
5577
|
+
process.stdout.write(JSON.stringify(report, null, 2) + `
|
|
5578
|
+
`);
|
|
5579
|
+
} else {
|
|
5580
|
+
process.stdout.write(`
|
|
5581
|
+
${BOLD2}${CYAN2}\uD83D\uDCA5 [Blast Radius Report] \uC704\uD5D8\uB3C4: ${report.overallRisk} (\uCD5C\uB300 \uC810\uC218: ${report.maxRiskScore})${RESET2}
|
|
5582
|
+
|
|
5583
|
+
` + ` \u2022 \uD30C\uAE09 \uB178\uB4DC \uC218 : ${report.affectedNodes.length}\uAC1C
|
|
5584
|
+
`);
|
|
5585
|
+
for (const n of report.affectedNodes) {
|
|
5586
|
+
process.stdout.write(` [H${n.hopLevel}] ${BOLD2}${n.name}${RESET2} (\uC704\uD5D8\uB3C4: ${n.riskScore}) - ${n.riskReason}
|
|
5587
|
+
`);
|
|
5588
|
+
}
|
|
5589
|
+
process.stdout.write(` \u2022 \uC0AC\uC804 \uD398\uCE58 \uBD88\uBCC0\uC2DD : ${report.prefetchedInvariants.length}\uAC74
|
|
5590
|
+
|
|
5591
|
+
`);
|
|
5592
|
+
}
|
|
5593
|
+
return 0;
|
|
5594
|
+
}
|
|
5595
|
+
if (subCommand === "auto-checkpoint") {
|
|
5596
|
+
let title;
|
|
5597
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5598
|
+
if (subArgs[i] === "--title" && subArgs[i + 1]) {
|
|
5599
|
+
title = subArgs[++i];
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
const synthesizer = new CausalSynthesizerEngine;
|
|
5603
|
+
const cp = await synthesizer.autoCheckpoint(process.cwd(), { title });
|
|
5604
|
+
if (options?.agentMode || options?.compact) {
|
|
5605
|
+
process.stdout.write(JSON.stringify({ ok: true, checkpoint: cp }) + `
|
|
5606
|
+
`);
|
|
5607
|
+
} else {
|
|
5608
|
+
process.stdout.write(`
|
|
5609
|
+
${BOLD2}${GREEN2}\u2728 [Git \uC758\uB3C4 \uC5ED\uD569\uC131 \u2794 \uC790\uB3D9 \uCCB4\uD06C\uD3EC\uC778\uD2B8 \uBC15\uC81C \uC644\uB8CC]${RESET2}
|
|
5610
|
+
` + ` \u2022 ID : ${BOLD2}${CYAN2}${cp.id}${RESET2}
|
|
5611
|
+
` + ` \u2022 \uC81C\uBAA9 : ${BOLD2}${cp.title}${RESET2}
|
|
5612
|
+
` + ` \u2022 \uBAA9\uD45C : ${cp.goal}
|
|
5613
|
+
` + ` \u2022 \uC644\uB8CC \uB2E8\uACC4 : ${cp.completedSteps.length}\uAC74 \uC790\uB3D9 \uCD94\uCD9C
|
|
5614
|
+
` + ` \u2022 \uB2E4\uC74C \uACFC\uC81C : ${cp.pendingTasks.length}\uAC74 \uC5ED\uD569\uC131
|
|
5615
|
+
|
|
5616
|
+
`);
|
|
5617
|
+
}
|
|
5618
|
+
return 0;
|
|
5619
|
+
}
|
|
5620
|
+
if (subCommand === "lens") {
|
|
5621
|
+
const lensAction = subArgs.find((a) => a !== "lens" && !a.startsWith("-")) || "sync";
|
|
5622
|
+
let vaultPath;
|
|
5623
|
+
for (let i = 0;i < subArgs.length; i++) {
|
|
5624
|
+
if (subArgs[i] === "--vault" && subArgs[i + 1]) {
|
|
5625
|
+
vaultPath = subArgs[++i];
|
|
5626
|
+
}
|
|
5627
|
+
}
|
|
5628
|
+
const lens = new CausalObsidianLensEngine;
|
|
5629
|
+
if (lensAction === "export") {
|
|
5630
|
+
const res = await lens.exportToVault(vaultPath);
|
|
5631
|
+
process.stdout.write(JSON.stringify(res, null, 2) + `
|
|
5632
|
+
`);
|
|
5633
|
+
return res.success ? 0 : 1;
|
|
5634
|
+
}
|
|
5635
|
+
if (lensAction === "import") {
|
|
5636
|
+
const res = await lens.importFromVault(vaultPath);
|
|
5637
|
+
process.stdout.write(JSON.stringify(res, null, 2) + `
|
|
5638
|
+
`);
|
|
5639
|
+
return res.success ? 0 : 1;
|
|
5640
|
+
}
|
|
5641
|
+
const syncRes = await lens.syncWithVault(vaultPath);
|
|
5642
|
+
process.stdout.write(JSON.stringify(syncRes, null, 2) + `
|
|
5643
|
+
`);
|
|
5644
|
+
return syncRes.success ? 0 : 1;
|
|
5645
|
+
}
|
|
2728
5646
|
process.stderr.write(`${RED2}\u274C \uC54C \uC218 \uC5C6\uB294 memory \uC11C\uBE0C \uBA85\uB839\uC5B4: '${subCommand}'${RESET2}
|
|
2729
5647
|
`);
|
|
2730
5648
|
printMemoryHelp();
|