engrm 0.4.18 → 0.4.21

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.
@@ -714,6 +714,16 @@ var MIGRATIONS = [
714
714
  ON tool_events(created_at_epoch DESC, id DESC);
715
715
  `
716
716
  },
717
+ {
718
+ version: 11,
719
+ description: "Add synced handoff metadata to session summaries",
720
+ sql: `
721
+ ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
722
+ ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
723
+ ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
724
+ ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
725
+ `
726
+ },
717
727
  {
718
728
  version: 11,
719
729
  description: "Add observation provenance from tool and prompt chronology",
@@ -1237,6 +1247,10 @@ class MemDatabase {
1237
1247
  p.name AS project_name,
1238
1248
  ss.request AS request,
1239
1249
  ss.completed AS completed,
1250
+ ss.capture_state AS capture_state,
1251
+ ss.recent_tool_names AS recent_tool_names,
1252
+ ss.hot_files AS hot_files,
1253
+ ss.recent_outcomes AS recent_outcomes,
1240
1254
  (SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
1241
1255
  (SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
1242
1256
  FROM sessions s
@@ -1251,6 +1265,10 @@ class MemDatabase {
1251
1265
  p.name AS project_name,
1252
1266
  ss.request AS request,
1253
1267
  ss.completed AS completed,
1268
+ ss.capture_state AS capture_state,
1269
+ ss.recent_tool_names AS recent_tool_names,
1270
+ ss.hot_files AS hot_files,
1271
+ ss.recent_outcomes AS recent_outcomes,
1254
1272
  (SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
1255
1273
  (SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
1256
1274
  FROM sessions s
@@ -1423,8 +1441,11 @@ class MemDatabase {
1423
1441
  completed: normalizeSummarySection(summary.completed),
1424
1442
  next_steps: normalizeSummarySection(summary.next_steps)
1425
1443
  };
1426
- const result = this.db.query(`INSERT INTO session_summaries (session_id, project_id, user_id, request, investigated, learned, completed, next_steps, created_at_epoch)
1427
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now);
1444
+ const result = this.db.query(`INSERT INTO session_summaries (
1445
+ session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
1446
+ capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
1447
+ )
1448
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
1428
1449
  const id = Number(result.lastInsertRowid);
1429
1450
  return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
1430
1451
  }
@@ -1439,7 +1460,11 @@ class MemDatabase {
1439
1460
  investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
1440
1461
  learned: normalizeSummarySection(summary.learned ?? existing.learned),
1441
1462
  completed: normalizeSummarySection(summary.completed ?? existing.completed),
1442
- next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
1463
+ next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
1464
+ capture_state: summary.capture_state ?? existing.capture_state,
1465
+ recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
1466
+ hot_files: summary.hot_files ?? existing.hot_files,
1467
+ recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
1443
1468
  };
1444
1469
  this.db.query(`UPDATE session_summaries
1445
1470
  SET project_id = ?,
@@ -1449,8 +1474,12 @@ class MemDatabase {
1449
1474
  learned = ?,
1450
1475
  completed = ?,
1451
1476
  next_steps = ?,
1477
+ capture_state = ?,
1478
+ recent_tool_names = ?,
1479
+ hot_files = ?,
1480
+ recent_outcomes = ?,
1452
1481
  created_at_epoch = ?
1453
- WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
1482
+ WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
1454
1483
  return this.getSessionSummary(summary.session_id);
1455
1484
  }
1456
1485
  getSessionSummary(sessionId) {
package/dist/server.js CHANGED
@@ -14120,6 +14120,16 @@ var MIGRATIONS = [
14120
14120
  ON tool_events(created_at_epoch DESC, id DESC);
14121
14121
  `
14122
14122
  },
14123
+ {
14124
+ version: 11,
14125
+ description: "Add synced handoff metadata to session summaries",
14126
+ sql: `
14127
+ ALTER TABLE session_summaries ADD COLUMN capture_state TEXT;
14128
+ ALTER TABLE session_summaries ADD COLUMN recent_tool_names TEXT;
14129
+ ALTER TABLE session_summaries ADD COLUMN hot_files TEXT;
14130
+ ALTER TABLE session_summaries ADD COLUMN recent_outcomes TEXT;
14131
+ `
14132
+ },
14123
14133
  {
14124
14134
  version: 11,
14125
14135
  description: "Add observation provenance from tool and prompt chronology",
@@ -14647,6 +14657,10 @@ class MemDatabase {
14647
14657
  p.name AS project_name,
14648
14658
  ss.request AS request,
14649
14659
  ss.completed AS completed,
14660
+ ss.capture_state AS capture_state,
14661
+ ss.recent_tool_names AS recent_tool_names,
14662
+ ss.hot_files AS hot_files,
14663
+ ss.recent_outcomes AS recent_outcomes,
14650
14664
  (SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
14651
14665
  (SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
14652
14666
  FROM sessions s
@@ -14661,6 +14675,10 @@ class MemDatabase {
14661
14675
  p.name AS project_name,
14662
14676
  ss.request AS request,
14663
14677
  ss.completed AS completed,
14678
+ ss.capture_state AS capture_state,
14679
+ ss.recent_tool_names AS recent_tool_names,
14680
+ ss.hot_files AS hot_files,
14681
+ ss.recent_outcomes AS recent_outcomes,
14664
14682
  (SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
14665
14683
  (SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
14666
14684
  FROM sessions s
@@ -14833,8 +14851,11 @@ class MemDatabase {
14833
14851
  completed: normalizeSummarySection(summary.completed),
14834
14852
  next_steps: normalizeSummarySection(summary.next_steps)
14835
14853
  };
14836
- const result = this.db.query(`INSERT INTO session_summaries (session_id, project_id, user_id, request, investigated, learned, completed, next_steps, created_at_epoch)
14837
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now);
14854
+ const result = this.db.query(`INSERT INTO session_summaries (
14855
+ session_id, project_id, user_id, request, investigated, learned, completed, next_steps,
14856
+ capture_state, recent_tool_names, hot_files, recent_outcomes, created_at_epoch
14857
+ )
14858
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(summary.session_id, summary.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, summary.capture_state ?? null, summary.recent_tool_names ?? null, summary.hot_files ?? null, summary.recent_outcomes ?? null, now);
14838
14859
  const id = Number(result.lastInsertRowid);
14839
14860
  return this.db.query("SELECT * FROM session_summaries WHERE id = ?").get(id);
14840
14861
  }
@@ -14849,7 +14870,11 @@ class MemDatabase {
14849
14870
  investigated: normalizeSummarySection(summary.investigated ?? existing.investigated),
14850
14871
  learned: normalizeSummarySection(summary.learned ?? existing.learned),
14851
14872
  completed: normalizeSummarySection(summary.completed ?? existing.completed),
14852
- next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps)
14873
+ next_steps: normalizeSummarySection(summary.next_steps ?? existing.next_steps),
14874
+ capture_state: summary.capture_state ?? existing.capture_state,
14875
+ recent_tool_names: summary.recent_tool_names ?? existing.recent_tool_names,
14876
+ hot_files: summary.hot_files ?? existing.hot_files,
14877
+ recent_outcomes: summary.recent_outcomes ?? existing.recent_outcomes
14853
14878
  };
14854
14879
  this.db.query(`UPDATE session_summaries
14855
14880
  SET project_id = ?,
@@ -14859,8 +14884,12 @@ class MemDatabase {
14859
14884
  learned = ?,
14860
14885
  completed = ?,
14861
14886
  next_steps = ?,
14887
+ capture_state = ?,
14888
+ recent_tool_names = ?,
14889
+ hot_files = ?,
14890
+ recent_outcomes = ?,
14862
14891
  created_at_epoch = ?
14863
- WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, now, summary.session_id);
14892
+ WHERE session_id = ?`).run(summary.project_id ?? existing.project_id, summary.user_id, normalized.request, normalized.investigated, normalized.learned, normalized.completed, normalized.next_steps, normalized.capture_state, normalized.recent_tool_names, normalized.hot_files, normalized.recent_outcomes, now, summary.session_id);
14864
14893
  return this.getSessionSummary(summary.session_id);
14865
14894
  }
14866
14895
  getSessionSummary(sessionId) {
@@ -16460,6 +16489,16 @@ function findStaleDecisionsGlobal(db, options) {
16460
16489
  function tokenizeProjectHint(text) {
16461
16490
  return Array.from(new Set((text.toLowerCase().match(/[a-z0-9_+-]{4,}/g) ?? []).filter(Boolean)));
16462
16491
  }
16492
+ function parseSummaryJsonList(value) {
16493
+ if (!value)
16494
+ return [];
16495
+ try {
16496
+ const parsed = JSON.parse(value);
16497
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
16498
+ } catch {
16499
+ return [];
16500
+ }
16501
+ }
16463
16502
  function isObservationRelatedToProject(obs, detected) {
16464
16503
  const hints = new Set([
16465
16504
  ...tokenizeProjectHint(detected.name),
@@ -16591,7 +16630,7 @@ function buildSessionContext(db, cwd, options = {}) {
16591
16630
  const recentToolEvents2 = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
16592
16631
  const recentSessions2 = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
16593
16632
  const projectTypeCounts2 = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
16594
- const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
16633
+ const recentOutcomes2 = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions2);
16595
16634
  return {
16596
16635
  project_name: projectName,
16597
16636
  canonical_id: canonicalId,
@@ -16629,7 +16668,7 @@ function buildSessionContext(db, cwd, options = {}) {
16629
16668
  const recentToolEvents = db.getRecentToolEvents(isNewProject ? null : projectId, isNewProject ? 8 : 6, opts.userId);
16630
16669
  const recentSessions = isNewProject ? [] : db.getRecentSessions(projectId, 5, opts.userId);
16631
16670
  const projectTypeCounts = isNewProject ? undefined : getProjectTypeCounts(db, projectId, opts.userId);
16632
- const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId);
16671
+ const recentOutcomes = isNewProject ? undefined : getRecentOutcomes(db, projectId, opts.userId, recentSessions);
16633
16672
  let securityFindings = [];
16634
16673
  if (!isNewProject) {
16635
16674
  try {
@@ -16972,7 +17011,7 @@ function getProjectTypeCounts(db, projectId, userId) {
16972
17011
  }
16973
17012
  return counts;
16974
17013
  }
16975
- function getRecentOutcomes(db, projectId, userId) {
17014
+ function getRecentOutcomes(db, projectId, userId, recentSessions) {
16976
17015
  const visibilityClause = userId ? " AND (sensitivity != 'personal' OR user_id = ?)" : "";
16977
17016
  const visibilityParams = userId ? [userId] : [];
16978
17017
  const summaries = db.db.query(`SELECT * FROM session_summaries
@@ -16982,6 +17021,15 @@ function getRecentOutcomes(db, projectId, userId) {
16982
17021
  const picked = [];
16983
17022
  const seen = new Set;
16984
17023
  for (const summary of summaries) {
17024
+ for (const item of parseSummaryJsonList(summary.recent_outcomes)) {
17025
+ const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
17026
+ if (!normalized || seen.has(normalized))
17027
+ continue;
17028
+ seen.add(normalized);
17029
+ picked.push(item);
17030
+ if (picked.length >= 5)
17031
+ return picked;
17032
+ }
16985
17033
  for (const line of [
16986
17034
  ...extractMeaningfulLines(summary.completed, 2),
16987
17035
  ...extractMeaningfulLines(summary.learned, 1)
@@ -16995,6 +17043,17 @@ function getRecentOutcomes(db, projectId, userId) {
16995
17043
  return picked;
16996
17044
  }
16997
17045
  }
17046
+ for (const session of recentSessions ?? []) {
17047
+ for (const item of parseSummaryJsonList(session.recent_outcomes)) {
17048
+ const normalized = item.toLowerCase().replace(/\s+/g, " ").trim();
17049
+ if (!normalized || seen.has(normalized))
17050
+ continue;
17051
+ seen.add(normalized);
17052
+ picked.push(item);
17053
+ if (picked.length >= 5)
17054
+ return picked;
17055
+ }
17056
+ }
16998
17057
  const rows = db.db.query(`SELECT * FROM observations
16999
17058
  WHERE project_id = ?
17000
17059
  AND lifecycle IN ('active', 'aging', 'pinned')
@@ -19011,10 +19070,20 @@ function mergeRemoteSummary(db, config2, change, projectId) {
19011
19070
  investigated: typeof change.metadata?.investigated === "string" ? change.metadata.investigated : null,
19012
19071
  learned: typeof change.metadata?.learned === "string" ? change.metadata.learned : null,
19013
19072
  completed: typeof change.metadata?.completed === "string" ? change.metadata.completed : null,
19014
- next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null
19073
+ next_steps: typeof change.metadata?.next_steps === "string" ? change.metadata.next_steps : null,
19074
+ capture_state: typeof change.metadata?.capture_state === "string" ? change.metadata.capture_state : null,
19075
+ recent_tool_names: encodeStringArray(change.metadata?.recent_tool_names),
19076
+ hot_files: encodeStringArray(change.metadata?.hot_files),
19077
+ recent_outcomes: encodeStringArray(change.metadata?.recent_outcomes)
19015
19078
  });
19016
19079
  return Boolean(summary);
19017
19080
  }
19081
+ function encodeStringArray(value) {
19082
+ if (!Array.isArray(value))
19083
+ return null;
19084
+ const normalized = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
19085
+ return normalized.length > 0 ? JSON.stringify(normalized) : null;
19086
+ }
19018
19087
  function normalizeRemoteObservationType(rawType, sourceId) {
19019
19088
  const type = typeof rawType === "string" ? rawType.trim().toLowerCase() : "";
19020
19089
  if (type === "bugfix" || type === "discovery" || type === "decision" || type === "pattern" || type === "change" || type === "feature" || type === "refactor" || type === "digest" || type === "standard" || type === "message") {
@@ -19817,7 +19886,7 @@ process.on("SIGTERM", () => {
19817
19886
  });
19818
19887
  var server = new McpServer({
19819
19888
  name: "engrm",
19820
- version: "0.4.18"
19889
+ version: "0.4.21"
19821
19890
  });
19822
19891
  server.tool("save_observation", "Save an observation to memory", {
19823
19892
  type: exports_external.enum([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engrm",
3
- "version": "0.4.18",
3
+ "version": "0.4.21",
4
4
  "description": "Shared memory across devices, sessions, and coding agents",
5
5
  "mcpName": "io.github.dr12hes/engrm",
6
6
  "type": "module",