@vedtechsolutions/engram-mcp 1.0.18 → 1.0.20

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.
@@ -11178,14 +11178,17 @@ function inferRole(nodeType, filePath, analysis) {
11178
11178
 
11179
11179
  // src/engines/curator.ts
11180
11180
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync, realpathSync as realpathSync2, lstatSync as lstatSync2, renameSync } from "fs";
11181
- import { join as join5, dirname as dirname3 } from "path";
11181
+ import { join as join5, dirname as dirname3, resolve as resolve3 } from "path";
11182
11182
  import { homedir as homedir3 } from "os";
11183
11183
  var logger14 = createLogger("curator");
11184
11184
  var lastBridgeWriteTime = 0;
11185
11185
  function discoverMemoryDir(cwd) {
11186
11186
  const envDir = process.env.CLAUDE_MEMORY_DIR;
11187
- if (envDir && existsSync5(envDir)) {
11188
- return envDir;
11187
+ if (envDir && envDir.startsWith("/") && !envDir.includes("\0") && envDir.length < 1e3) {
11188
+ const resolvedEnv = resolve3(envDir);
11189
+ if (resolvedEnv === resolve3(resolvedEnv) && existsSync5(resolvedEnv)) {
11190
+ return resolvedEnv;
11191
+ }
11189
11192
  }
11190
11193
  const home = homedir3();
11191
11194
  const projectsBase = join5(home, ".claude", "projects");
@@ -12672,4 +12675,4 @@ export {
12672
12675
  composeProjectUnderstanding,
12673
12676
  formatMentalModelInjection
12674
12677
  };
12675
- //# sourceMappingURL=chunk-OY2XHPUF.js.map
12678
+ //# sourceMappingURL=chunk-O3ZP4K3T.js.map
package/dist/hook.js CHANGED
@@ -174,10 +174,10 @@ import {
174
174
  updateReasoningChain,
175
175
  updateSelfModelFromSession,
176
176
  updateTask
177
- } from "./chunk-OY2XHPUF.js";
177
+ } from "./chunk-O3ZP4K3T.js";
178
178
 
179
179
  // src/hook.ts
180
- import { readFileSync, writeFileSync, existsSync, renameSync, statSync, readdirSync, unlinkSync, appendFileSync, openSync, readSync, closeSync } from "fs";
180
+ import { readFileSync, writeFileSync, existsSync, renameSync, statSync, readdirSync, unlinkSync, openSync, readSync, closeSync } from "fs";
181
181
  import { join, basename, resolve } from "path";
182
182
  import { homedir } from "os";
183
183
 
@@ -2886,15 +2886,39 @@ function readStdin() {
2886
2886
  return;
2887
2887
  }
2888
2888
  let data = "";
2889
+ const MAX_STDIN_BYTES = 10 * 1024 * 1024;
2889
2890
  process.stdin.setEncoding("utf-8");
2890
2891
  process.stdin.on("data", (chunk) => {
2891
2892
  data += chunk;
2893
+ if (data.length > MAX_STDIN_BYTES) {
2894
+ data = data.slice(0, MAX_STDIN_BYTES);
2895
+ process.stdin.destroy();
2896
+ resolve2(data);
2897
+ }
2892
2898
  });
2893
2899
  process.stdin.on("end", () => resolve2(data));
2894
2900
  process.stdin.on("error", () => resolve2(""));
2895
2901
  setTimeout(() => resolve2(data), 2e3);
2896
2902
  });
2897
2903
  }
2904
+ function validateTranscriptPath(p) {
2905
+ if (!p || typeof p !== "string") return null;
2906
+ const resolved = resolve(p);
2907
+ const allowed = join(homedir(), ".claude", "projects");
2908
+ if (!resolved.startsWith(allowed + "/") || !resolved.endsWith(".jsonl")) return null;
2909
+ return resolved;
2910
+ }
2911
+ function validateSessionId(sid) {
2912
+ if (!sid || typeof sid !== "string") return null;
2913
+ if (!/^[a-zA-Z0-9_-]{1,100}$/.test(sid)) return null;
2914
+ return sid;
2915
+ }
2916
+ function validateCwd(cwd) {
2917
+ if (!cwd || typeof cwd !== "string") return null;
2918
+ if (!cwd.startsWith("/")) return null;
2919
+ if (cwd.includes("\0") || cwd.length > 1e3) return null;
2920
+ return cwd;
2921
+ }
2898
2922
  function distillLesson(context) {
2899
2923
  const parts = [];
2900
2924
  if (context.errors && context.errors.length > 0 && context.fix) {
@@ -3244,11 +3268,22 @@ function sanitizeCognitiveState(state) {
3244
3268
  if (cog.current_approach && cog.current_approach.length < 5) {
3245
3269
  cog.current_approach = null;
3246
3270
  }
3247
- if (cog.current_approach && cog.current_approach.startsWith("Pre-compaction")) {
3248
- cog.current_approach = null;
3271
+ const templatePrefixes = ["Pre-compaction", "chronologically analyze", "This session is being continued"];
3272
+ if (cog.current_approach) {
3273
+ for (const prefix of templatePrefixes) {
3274
+ if (cog.current_approach.startsWith(prefix)) {
3275
+ cog.current_approach = null;
3276
+ break;
3277
+ }
3278
+ }
3249
3279
  }
3250
- if (cog.recent_discovery && cog.recent_discovery.startsWith("Pre-compaction")) {
3251
- cog.recent_discovery = null;
3280
+ if (cog.recent_discovery) {
3281
+ for (const prefix of templatePrefixes) {
3282
+ if (cog.recent_discovery.startsWith(prefix)) {
3283
+ cog.recent_discovery = null;
3284
+ break;
3285
+ }
3286
+ }
3252
3287
  }
3253
3288
  if (state.active_task && state.active_task.startsWith("<")) {
3254
3289
  state.active_task = null;
@@ -3335,6 +3370,12 @@ function writeSessionHandoff(state, narrative) {
3335
3370
  reasoning_trail: reasoningTrail.slice(0, 10)
3336
3371
  };
3337
3372
  try {
3373
+ const serialized = JSON.stringify(handoff, null, 2);
3374
+ if (serialized.length > 65536) {
3375
+ log.warn("Session handoff too large, truncating", { size: serialized.length });
3376
+ handoff.reasoning_trail = handoff.reasoning_trail.slice(0, 3);
3377
+ handoff.lessons = handoff.lessons.slice(0, 3);
3378
+ }
3338
3379
  const tmpPath = handoffPath + ".tmp";
3339
3380
  writeFileSync(tmpPath, JSON.stringify(handoff, null, 2), "utf-8");
3340
3381
  renameSync(tmpPath, handoffPath);
@@ -3455,16 +3496,16 @@ var [command, ...args] = process.argv.slice(2);
3455
3496
  async function main() {
3456
3497
  const stdinRaw = await readStdin();
3457
3498
  const stdinJson = safeParse(stdinRaw);
3458
- const stdinSessionId = stdinJson?.session_id;
3459
- if (stdinSessionId && typeof stdinSessionId === "string" && stdinSessionId.length > 0 && stdinSessionId.length <= 200) {
3460
- activeSessionId = stdinSessionId;
3499
+ const validatedSessionId = validateSessionId(stdinJson?.session_id);
3500
+ if (validatedSessionId) {
3501
+ activeSessionId = validatedSessionId;
3461
3502
  } else if (process.env.ENGRAM_SESSION_ID) {
3462
- activeSessionId = process.env.ENGRAM_SESSION_ID;
3503
+ const envSessionId = validateSessionId(process.env.ENGRAM_SESSION_ID);
3504
+ if (envSessionId) activeSessionId = envSessionId;
3463
3505
  }
3464
3506
  migrateLegacyWatcherState();
3465
3507
  try {
3466
- const stdinCwd = stdinJson?.cwd;
3467
- const cwdForDb = stdinCwd ?? process.cwd();
3508
+ const cwdForDb = validateCwd(stdinJson?.cwd) ?? process.cwd();
3468
3509
  const projectRoot = inferProjectPath(cwdForDb);
3469
3510
  const projectDbPath = deriveProjectDbPath(projectRoot);
3470
3511
  initProjectDatabase(projectDbPath);
@@ -3485,29 +3526,12 @@ async function main() {
3485
3526
  handlePostToolGeneric(stdinJson);
3486
3527
  break;
3487
3528
  case "notification": {
3488
- try {
3489
- appendFileSync(
3490
- join(homedir(), ".engram", "notification-debug.log"),
3491
- `[${(/* @__PURE__ */ new Date()).toISOString()}] NOTIFICATION stdin_keys=${Object.keys(stdinJson ?? {}).join(",")}
3492
- `
3493
- );
3494
- } catch {
3495
- }
3496
3529
  const notifType = stdinJson?.notification_type ?? args[0] ?? "general";
3497
3530
  const notifMessage = stdinJson?.message ?? args[1] ?? "";
3498
3531
  handleNotification(notifType, notifMessage);
3499
3532
  break;
3500
3533
  }
3501
3534
  case "session-start":
3502
- try {
3503
- const src = stdinJson?.source ?? "unknown";
3504
- appendFileSync(
3505
- join(homedir(), ".engram", "notification-debug.log"),
3506
- `[${(/* @__PURE__ */ new Date()).toISOString()}] SESSION-START source=${src} stdin_keys=${Object.keys(stdinJson ?? {}).join(",")}
3507
- `
3508
- );
3509
- } catch {
3510
- }
3511
3535
  handleSessionStart(stdinJson, args[0]);
3512
3536
  break;
3513
3537
  case "session-end":
@@ -3521,14 +3545,6 @@ async function main() {
3521
3545
  handleEngramUsed(stdinJson, args[0]);
3522
3546
  break;
3523
3547
  case "pre-compact":
3524
- try {
3525
- appendFileSync(
3526
- join(homedir(), ".engram", "notification-debug.log"),
3527
- `[${(/* @__PURE__ */ new Date()).toISOString()}] PRE-COMPACT stdin=${JSON.stringify(stdinJson).slice(0, 2e3)}
3528
- `
3529
- );
3530
- } catch {
3531
- }
3532
3548
  handlePreCompact();
3533
3549
  break;
3534
3550
  case "prompt-check":
@@ -4773,12 +4789,6 @@ function extractFilesFromToolCall(tool, input, output) {
4773
4789
  function handleNotification(type, data) {
4774
4790
  try {
4775
4791
  log.info("Notification received", { type, data: truncate(data, 300) });
4776
- try {
4777
- const debugLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] type=${type} data=${truncate(data, 500)}
4778
- `;
4779
- appendFileSync(join(homedir(), ".engram", "notification-debug.log"), debugLine);
4780
- } catch {
4781
- }
4782
4792
  const isContextWarning = type === "context_window" || type === "context" || /context.*(low|full|limit|running out|compact)/i.test(data) || /remaining.*context/i.test(data);
4783
4793
  if (isContextWarning) {
4784
4794
  log.info("Context pressure detected \u2014 proactive offload triggered", { type, data });
@@ -5984,7 +5994,7 @@ function handlePromptCheck(stdinJson, argFallback) {
5984
5994
  }
5985
5995
  try {
5986
5996
  if (state.total_turns > 0 && state.total_turns % CONTEXT_PRESSURE.MIN_TURNS_BETWEEN_CHECKS === 0) {
5987
- const transcriptPath = stdinJson?.transcript_path;
5997
+ const transcriptPath = validateTranscriptPath(stdinJson?.transcript_path);
5988
5998
  const remaining = estimateContextRemaining(transcriptPath);
5989
5999
  if (remaining !== null) {
5990
6000
  state.last_context_remaining = remaining;
@@ -6167,7 +6177,7 @@ function handlePromptCheck(stdinJson, argFallback) {
6167
6177
  } catch {
6168
6178
  }
6169
6179
  try {
6170
- const transcriptPath2 = stdinJson?.transcript_path;
6180
+ const transcriptPath2 = validateTranscriptPath(stdinJson?.transcript_path);
6171
6181
  if (transcriptPath2 && state.total_turns > 0 && state.total_turns - state.last_reasoning_extraction_turn >= TRANSCRIPT_REASONING.EXTRACTION_INTERVAL_TURNS && state.reasoning_extraction_count < TRANSCRIPT_REASONING.MAX_PER_SESSION && canEncodeReasoning(state)) {
6172
6182
  const reasoningSnippets = extractReasoningFromTranscript(transcriptPath2);
6173
6183
  for (const snippet of reasoningSnippets.slice(0, 2)) {
@@ -7175,7 +7185,7 @@ function handlePostCompact(stdinJson) {
7175
7185
  }
7176
7186
  }
7177
7187
  try {
7178
- const transcriptPath = stdinJson?.transcript_path ?? null;
7188
+ const transcriptPath = validateTranscriptPath(stdinJson?.transcript_path);
7179
7189
  if (transcriptPath) {
7180
7190
  const reasoningSnippets = extractReasoningFromTranscript(transcriptPath, TRANSCRIPT_REASONING.POST_COMPACT_MAX_MESSAGES);
7181
7191
  if (reasoningSnippets.length > 0) {
package/dist/index.js CHANGED
@@ -154,7 +154,7 @@ import {
154
154
  vaccinate,
155
155
  vacuumDatabase,
156
156
  validateMultiPerspective
157
- } from "./chunk-OY2XHPUF.js";
157
+ } from "./chunk-O3ZP4K3T.js";
158
158
 
159
159
  // src/index.ts
160
160
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vedtechsolutions/engram-mcp",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Cognitive memory system for AI — persistent, cross-session learning via MCP",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",