claude-mem-lite 3.28.1 → 3.28.3

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.28.1",
13
+ "version": "3.28.3",
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.28.1",
3
+ "version": "3.28.3",
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/format-utils.mjs CHANGED
@@ -24,12 +24,25 @@ export function truncate(str, max = 80) {
24
24
  return str.slice(0, end) + '\u2026';
25
25
  }
26
26
 
27
- // The block delimiters claude-mem-lite wraps injected context in. Any user-derived text
28
- // (observation title / lesson, handoff body) that contains one of these LITERALLY would
29
- // prematurely open or close the block it lands in, and the model then reads the rest as
30
- // undelimited context. Reachable by editing files that contain these tokens \u2014 e.g.
31
- // developing claude-mem-lite itself, where source/observations carry the delimiter names.
32
- const CONTEXT_DELIMITER_RE = /<\/?(?:claude-mem-context|memory-context|session-handoff)>/gi;
27
+ // Two delimiter classes are defanged here:
28
+ // 1. The blocks claude-mem-lite wraps injected context in (claude-mem-context /
29
+ // memory-context / session-handoff). User-derived text containing one LITERALLY
30
+ // would prematurely open/close the block it lands in, spilling the rest as
31
+ // undelimited context.
32
+ // 2. Harness-authority + tool-call tags the runtime injects (system-reminder /
33
+ // task-notification / function_calls / function_results, the latter two also in
34
+ // their antml:-namespaced form). Memory replays arbitrary captured text \u2014 file
35
+ // contents, tool output, web pages \u2014 so a poisoned observation carrying a literal
36
+ // <system-reminder>\u2026</system-reminder> or a forged <function_calls>\u2026</function_calls>
37
+ // block would smuggle a privileged-channel instruction / fake tool-call narrative
38
+ // into the model's context. It can't escape its wrapper (class 1 closers are
39
+ // defanged), but a nested forged authority/tool tag is still an indirect-prompt-
40
+ // injection vector; strip the brackets so it reads as inert text. Other tags
41
+ // (<other-tag>) \u2014 and the attribute-bearing <invoke \u2026>/<parameter \u2026> forms \u2014 are
42
+ // deliberately left intact.
43
+ // Reachable by editing files that contain these tokens \u2014 e.g. developing claude-mem-lite
44
+ // itself, where source/observations carry the delimiter names.
45
+ const CONTEXT_DELIMITER_RE = /<\/?(?:claude-mem-context|memory-context|session-handoff|system-reminder|task-notification|(?:antml:)?function_calls|(?:antml:)?function_results)>/gi;
33
46
 
34
47
  /**
35
48
  * Defang the literal context-block delimiter tags in user-derived text. Strips just the
@@ -134,8 +134,19 @@ function importToolPair(db, toolUse, toolResult, project) {
134
134
  * @param {{project: string}} opts
135
135
  * @returns {Promise<{prompts:number, observations:number, skipped:number, orphans:number}>}
136
136
  */
137
+ // Cold-start backfill reads the whole transcript into one JS string (the dedup
138
+ // state + the better-sqlite3 transaction below are synchronous, so a drop-in async
139
+ // stream isn't available). V8 caps a single string near 512MB, so a larger file
140
+ // throws a cryptic "Cannot create a string longer than…" mid-read. Fail early with
141
+ // an actionable message instead — well under that hard limit.
142
+ export const MAX_IMPORT_BYTES = 450 * 1024 * 1024;
143
+
137
144
  export async function importJsonl(db, path, { project }) {
138
- statSync(path);
145
+ const st = statSync(path);
146
+ if (st.size > MAX_IMPORT_BYTES) {
147
+ const mb = (n) => Math.round(n / (1024 * 1024));
148
+ throw new Error(`transcript too large (${mb(st.size)}MB > ${mb(MAX_IMPORT_BYTES)}MB cap); split it (e.g. split -l 50000 into parts) and import the parts`);
149
+ }
139
150
  const lines = readFileSync(path, 'utf8').split('\n');
140
151
  const seenPrompts = new Set();
141
152
  const seenObs = new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "3.28.1",
3
+ "version": "3.28.3",
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",
package/tier.mjs CHANGED
@@ -73,7 +73,14 @@ export const TIER_CASE_SQL = `(CASE
73
73
  WHEN type = 'bugfix' AND created_at_epoch >= ? THEN 'active'
74
74
  WHEN type = 'refactor' AND created_at_epoch >= ? THEN 'active'
75
75
  WHEN type = 'change' AND created_at_epoch >= ? THEN 'active'
76
- WHEN created_at_epoch >= ? THEN 'active'
76
+ -- Default-window fallthrough is for UNKNOWN/null types ONLY, mirroring
77
+ -- computeTier's ACTIVE_WINDOWS[type] with a DEFAULT fallback (a KNOWN type
78
+ -- never takes the fallback). Without the type guard, a known type whose window
79
+ -- was configured SHORTER than the default would wrongly re-qualify as 'active'
80
+ -- here after its own window expired — diverging from the JS classifier.
81
+ -- The "type IS NULL" arm keeps null-type rows on the default window (JS parity).
82
+ WHEN (type IS NULL OR type NOT IN ('decision','discovery','feature','bugfix','refactor','change'))
83
+ AND created_at_epoch >= ? THEN 'active'
77
84
  ELSE 'archive'
78
85
  END)`;
79
86