claude-mem-lite 3.52.0 → 3.53.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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "3.52.0",
13
+ "version": "3.53.0",
14
14
  "source": "./",
15
15
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark)."
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.52.0",
3
+ "version": "3.53.0",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/hook.mjs CHANGED
@@ -668,7 +668,13 @@ async function handleStop() {
668
668
  } else {
669
669
  const citedMain = extractCitationsFromTranscript(transcriptPath, { mainOnly: true });
670
670
  for (const id of citeBackIds) citedMain.add(id);
671
- const r = applyCitationDecay(db, project, injected, citedMain, sessionId);
671
+ // D#60: the idempotency key must be the CC session UUID, NOT the
672
+ // project-scoped memory sessionId — concurrent same-project CC
673
+ // sessions share the latter, so the second session's decay pass
674
+ // read "already decided" and silently undercounted decay_seen /
675
+ // streaks / adoption denominators. Fallback keeps legacy
676
+ // stdin-less invocations on the old key.
677
+ const r = applyCitationDecay(db, project, injected, citedMain, ccSessionId || sessionId);
672
678
  debugLog('DEBUG', 'handleStop', `citation-decay: touched=${r.touched} promoted=${r.promoted} demoted=${r.demoted}`);
673
679
  // R1: persist this session's invocation→cite funnel row. touched =
674
680
  // obs resolved this run (denominator), promoted = obs cited this run
@@ -599,9 +599,35 @@ export function applyCitationDecay(db, project, injectedIds, citedIds, sessionId
599
599
  const empty = { promoted: 0, demoted: 0, touched: 0 };
600
600
  if (process.env.MEM_DISABLE_CITATION_DECAY === '1') return empty;
601
601
  if (!db || !project || !sessionId) return empty;
602
- const injected = injectedIds instanceof Set ? injectedIds : new Set(injectedIds || []);
602
+ let injected = injectedIds instanceof Set ? injectedIds : new Set(injectedIds || []);
603
603
  if (injected.size === 0) return empty;
604
- const cited = citedIds instanceof Set ? citedIds : new Set(citedIds || []);
604
+ let cited = citedIds instanceof Set ? citedIds : new Set(citedIds || []);
605
+
606
+ // D#61: a lesson injected live and then superseded mid-session (auto-dedup /
607
+ // supersedes= save) leaves its citation crediting NOBODY — selectStmt below
608
+ // excludes superseded rows by design (defense-in-depth parity), so the keeper
609
+ // that now carries the lesson goes uncredited. Redirect such ids to their
610
+ // NUMERIC superseded_by keeper (one hop; superseded_by is polymorphic — the
611
+ // typeof guard mirrors timeline-core). Copies, not mutation: callers own the
612
+ // input sets.
613
+ const redirectStmt = db.prepare(
614
+ 'SELECT superseded_by FROM observations WHERE id = ? AND project = ? AND superseded_at IS NOT NULL'
615
+ );
616
+ const redirectSet = (set) => {
617
+ const out = new Set();
618
+ for (const id of set) {
619
+ const r = redirectStmt.get(id, project);
620
+ if (r && typeof r.superseded_by === 'number' && Number.isInteger(r.superseded_by)
621
+ && r.superseded_by > 0 && r.superseded_by !== id) {
622
+ out.add(r.superseded_by);
623
+ } else {
624
+ out.add(id);
625
+ }
626
+ }
627
+ return out;
628
+ };
629
+ injected = redirectSet(injected);
630
+ cited = redirectSet(cited);
605
631
 
606
632
  // Adoption gate (snapshot taken before any mutation this run). Suppress only
607
633
  // demotion; promotion always proceeds. Threshold overridable via env.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.52.0",
3
+ "version": "3.53.0",
4
4
  "description": "Persistent long-term memory for Claude Code via MCP — captures coding decisions, bugfixes, and context across sessions. Hybrid FTS5 + TF-IDF search with episode batching. Single SQLite DB, no external services. A lighter, lower-cost alternative to claude-mem (episode batching + a smaller model; cost savings are an internal estimate, not a measured benchmark).",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",