@vedtechsolutions/engram-mcp 1.0.20 → 1.0.22

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.
Files changed (2) hide show
  1. package/dist/hook.js +86 -11
  2. package/package.json +1 -1
package/dist/hook.js CHANGED
@@ -3250,13 +3250,29 @@ function sanitizeCognitiveState(state) {
3250
3250
  const cog = state.cognitive_state;
3251
3251
  if (!cog) return;
3252
3252
  const placeholders = ["X", "X.", "Y", "Y.", "Z", "Z."];
3253
- if (cog.current_approach && placeholders.includes(cog.current_approach)) {
3253
+ const templatePatterns = [
3254
+ /^X[\.\s]/,
3255
+ // Starts with X. or X<space>
3256
+ /^Approach:\s*X/i,
3257
+ // "Approach: X..."
3258
+ /Hypothesis:\s*[XYZ][\.\s]/,
3259
+ // Contains "Hypothesis: X."
3260
+ /Discovery:\s*[XYZ][\.\s]/,
3261
+ // Contains "Discovery: Z."
3262
+ /^[XYZ]\.\s+(?:Hypothesis|Discovery|Approach):/i
3263
+ // "X. Hypothesis: Y..."
3264
+ ];
3265
+ const isPlaceholderValue = (val) => {
3266
+ if (placeholders.includes(val)) return true;
3267
+ return templatePatterns.some((p) => p.test(val));
3268
+ };
3269
+ if (cog.current_approach && isPlaceholderValue(cog.current_approach)) {
3254
3270
  cog.current_approach = null;
3255
3271
  }
3256
- if (cog.active_hypothesis && placeholders.includes(cog.active_hypothesis)) {
3272
+ if (cog.active_hypothesis && isPlaceholderValue(cog.active_hypothesis)) {
3257
3273
  cog.active_hypothesis = null;
3258
3274
  }
3259
- if (cog.recent_discovery && placeholders.includes(cog.recent_discovery)) {
3275
+ if (cog.recent_discovery && isPlaceholderValue(cog.recent_discovery)) {
3260
3276
  cog.recent_discovery = null;
3261
3277
  }
3262
3278
  if (cog.active_hypothesis && cog.active_hypothesis.startsWith("/")) {
@@ -3285,11 +3301,35 @@ function sanitizeCognitiveState(state) {
3285
3301
  }
3286
3302
  }
3287
3303
  }
3304
+ if (cog.recent_discovery && cog.recent_discovery.length < 15 && !cog.recent_discovery.includes(" ")) {
3305
+ cog.recent_discovery = null;
3306
+ }
3307
+ if (cog.recent_discovery && /^that\s/i.test(cog.recent_discovery) && cog.recent_discovery.length < 40) {
3308
+ cog.recent_discovery = null;
3309
+ }
3288
3310
  if (state.active_task && state.active_task.startsWith("<")) {
3289
3311
  state.active_task = null;
3290
3312
  }
3313
+ if (state.active_task) {
3314
+ const conversationalPatterns = [
3315
+ /^i have another/i,
3316
+ /^just letting you/i,
3317
+ /^just a quick/i,
3318
+ /^now tell me/i,
3319
+ /^do another final/i,
3320
+ /^reviewing$/i
3321
+ // Generic tool-inferred task, not specific
3322
+ ];
3323
+ if (conversationalPatterns.some((p) => p.test(state.active_task))) {
3324
+ state.active_task = null;
3325
+ }
3326
+ }
3291
3327
  if (!state.active_task || state.active_task === "unknown task") {
3292
- const editedFiles = state.recent_actions.filter((a) => a.tool === "Edit" || a.tool === "Write").map((a) => a.target.split(/[/\\]/).pop() ?? a.target);
3328
+ const editedFiles = state.recent_actions.filter((a) => a.tool === "Edit" || a.tool === "Write").map((a) => {
3329
+ const arrowIdx = a.target.indexOf(" \u2192");
3330
+ const path = arrowIdx > 0 ? a.target.slice(0, arrowIdx) : a.target;
3331
+ return path.split(/[/\\]/).pop() ?? path;
3332
+ });
3293
3333
  const uniqueFiles = [...new Set(editedFiles)].slice(-5);
3294
3334
  if (uniqueFiles.length > 0) {
3295
3335
  if (cog.current_approach && cog.current_approach.length >= 10) {
@@ -3298,6 +3338,27 @@ function sanitizeCognitiveState(state) {
3298
3338
  state.active_task = `Working on ${uniqueFiles.join(", ")}`;
3299
3339
  }
3300
3340
  }
3341
+ if (!state.active_task && state.recent_prompts && state.recent_prompts.length > 0) {
3342
+ const taskVerbs = /^(fix|add|create|update|implement|remove|refactor|build|change|move|write|make|set|run|deploy|test|commit|push|install|configure|debug|solve|enable|disable|start|stop|check|verify|analyze|review|audit|optimize|clean|delete|migrate|upgrade|publish|get|do|finish)/i;
3343
+ for (let i = state.recent_prompts.length - 1; i >= 0; i--) {
3344
+ const prompt = state.recent_prompts[i];
3345
+ const first = prompt.split(/[.!?\n]/)[0]?.trim();
3346
+ if (first && first.length >= 15 && first.length <= 200 && taskVerbs.test(first)) {
3347
+ state.active_task = first;
3348
+ break;
3349
+ }
3350
+ }
3351
+ }
3352
+ }
3353
+ if (state.session_files.length > 0) {
3354
+ state.session_files = state.session_files.filter((f) => {
3355
+ if (f.length < 3 || f.length > 500) return false;
3356
+ if (!f.includes("/") && !f.includes("\\")) return false;
3357
+ if (f.includes("/../") || f.startsWith("../")) return false;
3358
+ if (/^\/?\d+\.\d+/.test(f)) return false;
3359
+ if (f === "/root/.ssh" || f.includes("/etc/cron")) return false;
3360
+ return true;
3361
+ });
3301
3362
  }
3302
3363
  }
3303
3364
  function deleteSessionState() {
@@ -5449,6 +5510,9 @@ function handleSubagentStop(stdinJson) {
5449
5510
  const fileMatches = lastMessage.match(/(?:\/[\w./+-]+\.\w+|[\w./+-]+\.(?:ts|js|py|xml|json|css|scss|md))/g);
5450
5511
  if (fileMatches) {
5451
5512
  for (const f of fileMatches) {
5513
+ if (!f.includes("/")) continue;
5514
+ if (f.includes("/../") || f.startsWith("../") || /^\/?\d+\.\d+/.test(f)) continue;
5515
+ if (f === "/root/.ssh" || f.includes("/etc/cron")) continue;
5452
5516
  if (!state.session_files.includes(f)) {
5453
5517
  state.session_files.push(f);
5454
5518
  }
@@ -5457,7 +5521,7 @@ function handleSubagentStop(stdinJson) {
5457
5521
  state.session_files = state.session_files.slice(-50);
5458
5522
  }
5459
5523
  }
5460
- const isMetaAnalysis = lastMessage.startsWith("<analysis>") || lastMessage.startsWith("<summary>") || /^#+\s/.test(lastMessage) || lastMessage.startsWith("Based on ");
5524
+ const isMetaAnalysis = lastMessage.startsWith("<analysis>") || lastMessage.startsWith("<summary>") || /^#+\s/.test(lastMessage) || lastMessage.startsWith("Based on ") || /^(I now have|Perfect!|I have (sufficient|enough|comprehensive))/i.test(lastMessage) || /^(Here('s| is) (the|my|a) (comprehensive|complete|detailed|full))/i.test(lastMessage) || lastMessage.includes("## ") && lastMessage.length > 500;
5461
5525
  const hasError = !isMetaAnalysis && containsError(lastMessage);
5462
5526
  if (hasError) {
5463
5527
  state.recent_errors.push(truncate(lastMessage, 200));
@@ -5696,13 +5760,20 @@ function buildContinuationBrief(state) {
5696
5760
  for (const id of state.decision_memory_ids.slice(-5)) {
5697
5761
  try {
5698
5762
  const mem = getMemory(id);
5699
- if (mem) decisions.push(truncate(mem.content, 150));
5763
+ if (mem) {
5764
+ const content = mem.content;
5765
+ if (/^Delegated:|^Decision:\s*Delegated/i.test(content)) continue;
5766
+ decisions.push(truncate(content, 150));
5767
+ }
5700
5768
  } catch {
5701
5769
  }
5702
5770
  }
5703
5771
  const triedFailed = (state.session_outcomes ?? []).filter((o) => o.includes("\u2192 fail") || o.includes("\u2192 dead end") || o.includes("\u2192 blocked")).slice(-5).map((o) => truncate(o, 120));
5704
5772
  const editActions = state.recent_actions.filter((a) => a.tool === "Edit" || a.tool === "Write");
5705
- const keyFiles = [...new Set(editActions.map((a) => a.target))].slice(-10);
5773
+ const keyFiles = [...new Set(editActions.map((a) => {
5774
+ const arrowIdx = a.target.indexOf(" \u2192");
5775
+ return arrowIdx > 0 ? a.target.slice(0, arrowIdx) : a.target;
5776
+ }))].slice(-10);
5706
5777
  const recentBash = state.recent_commands?.slice(-5) ?? [];
5707
5778
  if (recentBash.length > 0) {
5708
5779
  const bashSummary = recentBash.map((c) => truncate(c.cmd, 80));
@@ -6334,7 +6405,7 @@ ${distillLines}`
6334
6405
  }
6335
6406
  if (content.length >= 30 && content.length <= 500 && !content.startsWith("<")) {
6336
6407
  const firstSentence = content.split(/[.!?\n]/)[0]?.trim();
6337
- const isTaskLike = firstSentence && /^(fix|add|create|update|implement|remove|refactor|build|change|move|write|make|set|run|deploy|test|commit|push|install|configure|debug|solve|close|merge|release)/i.test(firstSentence);
6408
+ const isTaskLike = firstSentence && /^(fix|add|create|update|implement|remove|refactor|build|change|move|write|make|set|run|deploy|test|commit|push|install|configure|debug|solve|close|merge|release|enable|disable|start|stop|check|verify|analyze|review|audit|optimize|clean|delete|migrate|upgrade|publish|get|do|finish)/i.test(firstSentence);
6338
6409
  if (isTaskLike && firstSentence.length >= 15 && firstSentence.length <= 200) {
6339
6410
  state.active_task = firstSentence;
6340
6411
  }
@@ -7131,10 +7202,14 @@ function handlePostCompact(stdinJson) {
7131
7202
  if (!recovery) return;
7132
7203
  const budget = new OutputBudget(OUTPUT_BUDGET.POST_COMPACT_MAX_BYTES);
7133
7204
  const lines = [];
7134
- const brief = state.continuation_brief;
7135
- if (brief) {
7205
+ sanitizeCognitiveState(state);
7206
+ const brief = buildContinuationBrief(state);
7207
+ const briefUsable = brief.task !== "unknown task" || brief.last_actions.length > 0;
7208
+ if (briefUsable) {
7136
7209
  const mindLines = ["[Engram] Continue from where you left off:"];
7137
- mindLines.push(` Task: ${brief.task}`);
7210
+ if (brief.task !== "unknown task") {
7211
+ mindLines.push(` Task: ${brief.task}`);
7212
+ }
7138
7213
  mindLines.push(` Phase: ${brief.phase}`);
7139
7214
  if (brief.last_actions.length > 0) {
7140
7215
  mindLines.push(` Last actions:`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vedtechsolutions/engram-mcp",
3
- "version": "1.0.20",
3
+ "version": "1.0.22",
4
4
  "description": "Cognitive memory system for AI — persistent, cross-session learning via MCP",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",