@vedtechsolutions/engram-mcp 1.0.6 → 1.0.7

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 +139 -30
  2. package/package.json +1 -1
package/dist/hook.js CHANGED
@@ -1793,8 +1793,18 @@ function extractGoal(params) {
1793
1793
  const topFiles = params.session_files.slice(-3).map((f) => f.split(/[/\\]/).pop() ?? f).join(", ");
1794
1794
  return `${params.cognitive_state.recent_discovery} (${topFiles})`;
1795
1795
  }
1796
+ if (params.session_files && params.session_files.length > 0) {
1797
+ const topFiles = params.session_files.slice(-5).map((f) => f.split(/[/\\]/).pop() ?? f).join(", ");
1798
+ return `modifying ${topFiles}`;
1799
+ }
1796
1800
  if (params.conversation.topic_history.length > 0) {
1797
- return params.conversation.topic_history[0].topic;
1801
+ const topic = params.conversation.topic_history[0].topic;
1802
+ const commaCount = (topic.match(/,/g) || []).length;
1803
+ const wordCount = topic.split(/\s+/).length;
1804
+ const isVagueKeywordList = commaCount >= 2 && wordCount <= commaCount + 2;
1805
+ if (!isVagueKeywordList) {
1806
+ return topic;
1807
+ }
1798
1808
  }
1799
1809
  return null;
1800
1810
  }
@@ -3127,7 +3137,9 @@ function loadWatcherState() {
3127
3137
  offload_message_sent: raw.offload_message_sent ?? false,
3128
3138
  summary_injection_mode: raw.summary_injection_mode ?? false,
3129
3139
  recent_commands: raw.recent_commands ?? [],
3130
- procedural_encoded_count: raw.procedural_encoded_count ?? 0
3140
+ procedural_encoded_count: raw.procedural_encoded_count ?? 0,
3141
+ recent_actions: raw.recent_actions ?? [],
3142
+ continuation_brief: raw.continuation_brief ?? null
3131
3143
  };
3132
3144
  }
3133
3145
  } catch {
@@ -3191,7 +3203,9 @@ function loadWatcherState() {
3191
3203
  offload_message_sent: false,
3192
3204
  summary_injection_mode: false,
3193
3205
  recent_commands: [],
3194
- procedural_encoded_count: 0
3206
+ procedural_encoded_count: 0,
3207
+ recent_actions: [],
3208
+ continuation_brief: null
3195
3209
  };
3196
3210
  }
3197
3211
  function saveWatcherState(state) {
@@ -3628,6 +3642,14 @@ Output: ${truncate(toolOutput, 500)}`,
3628
3642
  if (state.recent_tool_names.length > 10) {
3629
3643
  state.recent_tool_names = state.recent_tool_names.slice(-10);
3630
3644
  }
3645
+ state.recent_actions.push({
3646
+ tool: "Bash",
3647
+ target: truncate(cmd, 120),
3648
+ time: (/* @__PURE__ */ new Date()).toISOString()
3649
+ });
3650
+ if (state.recent_actions.length > 15) {
3651
+ state.recent_actions = state.recent_actions.slice(-15);
3652
+ }
3631
3653
  stateChanged = true;
3632
3654
  let testRun = null;
3633
3655
  if (isTestCommand(cmd)) {
@@ -4111,6 +4133,14 @@ function handlePostWrite(toolInput, argFallback) {
4111
4133
  const filePath = input?.file_path ?? input?.path ?? "";
4112
4134
  if (!filePath) return;
4113
4135
  const state = loadWatcherState();
4136
+ state.recent_actions.push({
4137
+ tool: "Edit",
4138
+ target: filePath,
4139
+ time: (/* @__PURE__ */ new Date()).toISOString()
4140
+ });
4141
+ if (state.recent_actions.length > 15) {
4142
+ state.recent_actions = state.recent_actions.slice(-15);
4143
+ }
4114
4144
  if (typeof filePath === "string" && !state.session_files.includes(filePath)) {
4115
4145
  state.session_files.push(filePath);
4116
4146
  if (state.session_files.length > 50) {
@@ -4254,6 +4284,16 @@ function handlePostToolGeneric(stdinJson) {
4254
4284
  if (state.recent_tool_names.length > 10) {
4255
4285
  state.recent_tool_names = state.recent_tool_names.slice(-10);
4256
4286
  }
4287
+ if (toolName !== "Read" && toolName !== "Glob" && toolName !== "Grep") {
4288
+ state.recent_actions.push({
4289
+ tool: toolName,
4290
+ target: call.input_summary ?? "",
4291
+ time: (/* @__PURE__ */ new Date()).toISOString()
4292
+ });
4293
+ if (state.recent_actions.length > 15) {
4294
+ state.recent_actions = state.recent_actions.slice(-15);
4295
+ }
4296
+ }
4257
4297
  state.reasoning_buffer.push(call);
4258
4298
  if (state.reasoning_buffer.length > REASONING_TRACE.MAX_BUFFER_SIZE) {
4259
4299
  state.reasoning_buffer = state.reasoning_buffer.slice(-REASONING_TRACE.MAX_BUFFER_SIZE);
@@ -4940,7 +4980,9 @@ function handleSessionStart(stdinJson, argFallback) {
4940
4980
  offload_message_sent: false,
4941
4981
  summary_injection_mode: false,
4942
4982
  recent_commands: isPostCompact ? prevState?.recent_commands ?? [] : [],
4943
- procedural_encoded_count: isPostCompact ? prevState?.procedural_encoded_count ?? 0 : 0
4983
+ procedural_encoded_count: isPostCompact ? prevState?.procedural_encoded_count ?? 0 : 0,
4984
+ recent_actions: isPostCompact ? prevState?.recent_actions ?? [] : [],
4985
+ continuation_brief: isPostCompact ? prevState?.continuation_brief ?? null : null
4944
4986
  });
4945
4987
  const source = metadata.source;
4946
4988
  if (!source || source === "startup") {
@@ -5540,6 +5582,50 @@ function handleEngramUsed(stdinJson, argFallback) {
5540
5582
  }
5541
5583
  saveWatcherState(state);
5542
5584
  }
5585
+ function buildContinuationBrief(state) {
5586
+ const cog = state.cognitive_state;
5587
+ const task = state.active_task ?? cog.current_approach ?? (state.session_files.length > 0 ? `Working on ${state.session_files.slice(-3).map((f) => f.split(/[/\\]/).pop() ?? f).join(", ")}` : "unknown task");
5588
+ const lastActions = state.recent_actions.slice(-8).map((a) => {
5589
+ const fileName = a.target.includes("/") ? a.target.split(/[/\\]/).pop() ?? a.target : a.target;
5590
+ return `${a.tool}: ${truncate(fileName, 80)}`;
5591
+ });
5592
+ const nextSteps = [];
5593
+ if (cog.search_intent && !nextSteps.some((s) => s.includes(cog.search_intent))) {
5594
+ nextSteps.push(`Investigate: ${cog.search_intent}`);
5595
+ }
5596
+ const lastAction = state.recent_actions[state.recent_actions.length - 1];
5597
+ if (lastAction && nextSteps.length === 0) {
5598
+ if (lastAction.tool === "Edit" || lastAction.tool === "Write") {
5599
+ nextSteps.push("Run tests to verify changes");
5600
+ } else if (lastAction.tool === "Bash" && lastAction.target.includes("test")) {
5601
+ nextSteps.push("Review test results and commit if passing");
5602
+ }
5603
+ }
5604
+ if (state.recent_errors.length > 0 && nextSteps.length < 3) {
5605
+ nextSteps.push(`Fix: ${truncate(state.recent_errors[state.recent_errors.length - 1], 120)}`);
5606
+ }
5607
+ const decisions = [];
5608
+ for (const id of state.decision_memory_ids.slice(-5)) {
5609
+ try {
5610
+ const mem = getMemory(id);
5611
+ if (mem) decisions.push(truncate(mem.content, 150));
5612
+ } catch {
5613
+ }
5614
+ }
5615
+ 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));
5616
+ const editActions = state.recent_actions.filter((a) => a.tool === "Edit" || a.tool === "Write");
5617
+ const keyFiles = [...new Set(editActions.map((a) => a.target))].slice(-10);
5618
+ return {
5619
+ task: truncate(task, 300),
5620
+ phase: cog.session_phase ?? "unknown",
5621
+ last_actions: lastActions,
5622
+ next_steps: nextSteps.slice(0, 5),
5623
+ decisions,
5624
+ tried_failed: triedFailed,
5625
+ key_files: keyFiles.length > 0 ? keyFiles : state.session_files.slice(-10),
5626
+ blockers: state.recent_errors.slice(-3).map((e) => truncate(e, 150))
5627
+ };
5628
+ }
5543
5629
  function handlePreCompact() {
5544
5630
  const state = loadWatcherState();
5545
5631
  if (!state.active_project) {
@@ -5782,6 +5868,10 @@ ${summaryParts.join(". ")}` : `Pre-compaction session summary: ${summaryParts.jo
5782
5868
  });
5783
5869
  process.stdout.write(output + "\n");
5784
5870
  }
5871
+ try {
5872
+ state.continuation_brief = buildContinuationBrief(state);
5873
+ } catch {
5874
+ }
5785
5875
  state.recovery_context = recovery;
5786
5876
  state.understanding_snapshot = understanding;
5787
5877
  saveWatcherState(state);
@@ -6173,6 +6263,7 @@ ${distillLines}`
6173
6263
  budget.append("antipatterns", warnings);
6174
6264
  state.pending_error_warnings = [];
6175
6265
  }
6266
+ const surfacedIds = /* @__PURE__ */ new Set();
6176
6267
  try {
6177
6268
  const surfaceDomain = state.active_domain;
6178
6269
  if (surfaceDomain && surfaceDomain.length >= MEMORY_SURFACE.MIN_DOMAIN_LENGTH) {
@@ -6205,6 +6296,7 @@ ${distillLines}`
6205
6296
  }
6206
6297
  state.surface_injection_turns[mem.id] = state.total_turns;
6207
6298
  state.proactive_injection_turns[mem.id] = state.total_turns;
6299
+ surfacedIds.add(mem.id);
6208
6300
  }
6209
6301
  budget.append("surface", surfaceLines.join("\n"));
6210
6302
  }
@@ -6272,6 +6364,7 @@ ${distillLines}`
6272
6364
  );
6273
6365
  for (const m of result.memories) {
6274
6366
  if (somaticIds.has(m.memory.id)) continue;
6367
+ if (surfacedIds.has(m.memory.id)) continue;
6275
6368
  if (isRecallNoise(m.memory.content, m.memory.type, m.memory.tags)) continue;
6276
6369
  if (m.memory.tags.includes("pre-compact")) continue;
6277
6370
  if (m.memory.tags.includes("session_narrative")) continue;
@@ -6919,38 +7012,54 @@ function handlePostCompact(stdinJson) {
6919
7012
  if (!recovery) return;
6920
7013
  const budget = new OutputBudget(OUTPUT_BUDGET.POST_COMPACT_MAX_BYTES);
6921
7014
  const lines = [];
6922
- const cog = state.cognitive_state;
6923
- const cogCtx = recovery.working_state?.cognitive_context;
6924
- const hasAnyCognitive = cog?.current_approach || cog?.active_hypothesis || cog?.recent_discovery || cogCtx?.planned_next_step || recovery.continuation_hint;
6925
- if (hasAnyCognitive) {
6926
- const mindLines = ["[Engram] Before compaction, you were:"];
6927
- if (state.active_task) mindLines.push(` Task: ${truncate(state.active_task, 200)}`);
6928
- if (cog?.session_phase) mindLines.push(` Phase: ${cog.session_phase}`);
6929
- if (cog?.current_approach) mindLines.push(` Approach: ${truncate(cog.current_approach, 300)}`);
6930
- if (cog?.active_hypothesis) mindLines.push(` Hypothesis: ${truncate(cog.active_hypothesis, 300)}`);
6931
- if (cog?.recent_discovery) mindLines.push(` Discovery: ${truncate(cog.recent_discovery, 300)}`);
6932
- if (cogCtx?.planned_next_step) mindLines.push(` Next step: ${truncate(cogCtx.planned_next_step, 200)}`);
6933
- if (cog?.search_intent) mindLines.push(` Investigating: ${truncate(cog.search_intent, 200)}`);
6934
- const ruledOut = (state.session_outcomes ?? []).filter((o) => o.includes("\u2192 fail") || o.includes("\u2192 dead end") || o.includes("\u2192 blocked"));
6935
- if (ruledOut.length > 0) {
7015
+ const brief = state.continuation_brief;
7016
+ if (brief) {
7017
+ const mindLines = ["[Engram] Continue from where you left off:"];
7018
+ mindLines.push(` Task: ${brief.task}`);
7019
+ mindLines.push(` Phase: ${brief.phase}`);
7020
+ if (brief.last_actions.length > 0) {
7021
+ mindLines.push(` Last actions:`);
7022
+ for (const a of brief.last_actions.slice(-5)) mindLines.push(` - ${a}`);
7023
+ }
7024
+ if (brief.next_steps.length > 0) {
7025
+ mindLines.push(` Next steps:`);
7026
+ for (const s of brief.next_steps) mindLines.push(` - ${s}`);
7027
+ }
7028
+ if (brief.decisions.length > 0) {
7029
+ mindLines.push(` Decisions made:`);
7030
+ for (const d of brief.decisions) mindLines.push(` - ${d}`);
7031
+ }
7032
+ if (brief.tried_failed.length > 0) {
6936
7033
  mindLines.push(` Already tried (didn't work):`);
6937
- for (const o of ruledOut.slice(-3)) mindLines.push(` - ${truncate(o, 150)}`);
6938
- }
6939
- const blockers = recovery.working_state?.active_blockers ?? [];
6940
- if (blockers.length > 0) {
6941
- mindLines.push(` Blockers: ${blockers.slice(0, 3).map((b) => truncate(b, 100)).join("; ")}`);
7034
+ for (const t of brief.tried_failed) mindLines.push(` - ${t}`);
6942
7035
  }
6943
- if (state.recent_errors.length > 0) {
6944
- mindLines.push(` Recent errors: ${state.recent_errors.slice(-2).map((e) => truncate(e, 100)).join("; ")}`);
7036
+ if (brief.blockers.length > 0) {
7037
+ mindLines.push(` Blockers: ${brief.blockers.join("; ")}`);
6945
7038
  }
6946
- if (state.session_files.length > 0) {
6947
- const files = state.session_files.slice(-8).map((f) => f.split(/[/\\]/).pop() ?? f);
7039
+ if (brief.key_files.length > 0) {
7040
+ const files = brief.key_files.map((f) => f.split(/[/\\]/).pop() ?? f);
6948
7041
  mindLines.push(` Files: ${files.join(", ")}`);
6949
7042
  }
6950
- if (recovery.continuation_hint) {
6951
- mindLines.push(` Continue: ${truncate(recovery.continuation_hint, 300)}`);
6952
- }
6953
7043
  lines.push(mindLines.join("\n"));
7044
+ } else {
7045
+ const cog = state.cognitive_state;
7046
+ const cogCtx = recovery.working_state?.cognitive_context;
7047
+ const hasAnyCognitive = cog?.current_approach || cog?.active_hypothesis || cog?.recent_discovery || cogCtx?.planned_next_step || recovery.continuation_hint;
7048
+ if (hasAnyCognitive) {
7049
+ const mindLines = ["[Engram] Before compaction, you were:"];
7050
+ if (state.active_task) mindLines.push(` Task: ${truncate(state.active_task, 200)}`);
7051
+ if (cog?.session_phase) mindLines.push(` Phase: ${cog.session_phase}`);
7052
+ if (cog?.current_approach) mindLines.push(` Approach: ${truncate(cog.current_approach, 300)}`);
7053
+ if (cog?.active_hypothesis) mindLines.push(` Hypothesis: ${truncate(cog.active_hypothesis, 300)}`);
7054
+ if (cog?.recent_discovery) mindLines.push(` Discovery: ${truncate(cog.recent_discovery, 300)}`);
7055
+ if (cogCtx?.planned_next_step) mindLines.push(` Next step: ${truncate(cogCtx.planned_next_step, 200)}`);
7056
+ if (recovery.continuation_hint) mindLines.push(` Continue: ${truncate(recovery.continuation_hint, 300)}`);
7057
+ if (state.session_files.length > 0) {
7058
+ const files = state.session_files.slice(-8).map((f) => f.split(/[/\\]/).pop() ?? f);
7059
+ mindLines.push(` Files: ${files.join(", ")}`);
7060
+ }
7061
+ lines.push(mindLines.join("\n"));
7062
+ }
6954
7063
  }
6955
7064
  try {
6956
7065
  const transcriptPath = stdinJson?.transcript_path ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vedtechsolutions/engram-mcp",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Cognitive memory system for AI — persistent, cross-session learning via MCP",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",