claude-mem-lite 2.39.1 → 2.40.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/mem-cli.mjs +10 -2
- package/package.json +1 -1
- package/scripts/pre-tool-recall.js +5 -0
- package/server.mjs +12 -4
package/mem-cli.mjs
CHANGED
|
@@ -172,6 +172,9 @@ function cmdSearch(db, args) {
|
|
|
172
172
|
const effectiveSource = source || ((type || tier || minImportance) ? 'observations' : null);
|
|
173
173
|
|
|
174
174
|
const results = [];
|
|
175
|
+
// Tracks whether AND returned 0 and OR recovered non-empty. Mirrors server.mjs
|
|
176
|
+
// ctx.orFallbackFired so the header can surface a "(relaxed AND→OR)" hint.
|
|
177
|
+
let orFallbackFired = false;
|
|
175
178
|
|
|
176
179
|
// Search observations
|
|
177
180
|
if (!effectiveSource || effectiveSource === 'observations') {
|
|
@@ -179,7 +182,10 @@ function cmdSearch(db, args) {
|
|
|
179
182
|
if (obsRows.length === 0) {
|
|
180
183
|
const orQuery = relaxFtsQueryToOr(ftsQuery);
|
|
181
184
|
if (orQuery) {
|
|
182
|
-
try {
|
|
185
|
+
try {
|
|
186
|
+
obsRows = searchFts(db, orQuery, { type, project, limit, dateFrom, dateTo, minImportance, branch, includeNoise, offset: effectiveSource ? offset : 0 });
|
|
187
|
+
if (obsRows.length > 0) orFallbackFired = true;
|
|
188
|
+
} catch {}
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
// Type-list fallback
|
|
@@ -388,7 +394,9 @@ function cmdSearch(db, args) {
|
|
|
388
394
|
|
|
389
395
|
const showTime = sort === 'time';
|
|
390
396
|
const hasMixed = paged.some(r => r._source === 'session' || r._source === 'prompt');
|
|
391
|
-
|
|
397
|
+
// Suppressed when --or was explicit — user already asked for OR, no "fallback" there.
|
|
398
|
+
const fallbackHint = orFallbackFired && !useOr ? ' (relaxed AND→OR)' : '';
|
|
399
|
+
out(`[mem] ${paged.length} result${paged.length !== 1 ? 's' : ''} for "${query}"${fallbackHint}:${hasMixed ? ' (# observation, S# session, P# prompt)' : ''}`);
|
|
392
400
|
for (const r of paged) {
|
|
393
401
|
const timeStr = showTime && r.created_at_epoch ? ` (${relativeTime(r.created_at_epoch)})` : '';
|
|
394
402
|
if (r._source === 'session') {
|
package/package.json
CHANGED
|
@@ -228,6 +228,10 @@ try {
|
|
|
228
228
|
// but the total payload is bounded by the 3-row limit and the cooldown.
|
|
229
229
|
const LESSON_MAX = isRead ? 120 : 240;
|
|
230
230
|
if (allRows.length > 0) {
|
|
231
|
+
// Framing line mirrors #7758 handoff-injection fix: without an explicit
|
|
232
|
+
// "system-injected, continue" disclaimer, observed turn-end after Edit+reminder
|
|
233
|
+
// when the model misreads passive lesson context as a closing note.
|
|
234
|
+
lines.push(`[mem] PreToolUse recall — system-injected context, continue your planned action:`);
|
|
231
235
|
lines.push(`[mem] Lessons for ${fname}:`);
|
|
232
236
|
for (const r of allRows) {
|
|
233
237
|
if (r.lesson_learned) {
|
|
@@ -250,6 +254,7 @@ try {
|
|
|
250
254
|
// v2.34.6: Read does NOT emit this nudge. Read is passive — the agent
|
|
251
255
|
// isn't necessarily about to solve anything, so /lesson prompts are noise.
|
|
252
256
|
// Empty Reads exit silently, saving ~60 tokens × (every empty-file Read).
|
|
257
|
+
lines.push(`[mem] PreToolUse recall — system-injected context, continue your planned action:`);
|
|
253
258
|
lines.push(`[mem] No prior lessons for ${fname} — if you solve a non-obvious bug here, run: /lesson --file ${fname} "<root cause + fix>"`);
|
|
254
259
|
}
|
|
255
260
|
|
package/server.mjs
CHANGED
|
@@ -235,13 +235,17 @@ function searchObservations(ctx) {
|
|
|
235
235
|
.all(...buildObsFtsParams({ now, projectBoost, ftsQuery, args, epochFrom, epochTo, limit: perSourceLimit, offset: perSourceOffset }));
|
|
236
236
|
for (const r of rows) results.push(ftsRowToResult(r, { snippet: true }));
|
|
237
237
|
|
|
238
|
-
// OR fallback: when AND query returns 0 results, retry with OR semantics
|
|
238
|
+
// OR fallback: when AND query returns 0 results, retry with OR semantics.
|
|
239
|
+
// Sets ctx.orFallbackFired so the top-level formatter can surface a "relaxed
|
|
240
|
+
// AND→OR" hint — without it, callers can't distinguish a strict multi-term
|
|
241
|
+
// match from a partial single-term recovery.
|
|
239
242
|
if (rows.length === 0) {
|
|
240
243
|
const orQuery = relaxFtsQueryToOr(ftsQuery);
|
|
241
244
|
if (orQuery) {
|
|
242
245
|
try {
|
|
243
246
|
const orRows = db.prepare(buildObsFtsQuery('full', { multiplier: 0.5, withSnippet: true, withOffset: true, includeNoise }))
|
|
244
247
|
.all(...buildObsFtsParams({ now, projectBoost, ftsQuery: orQuery, args, epochFrom, epochTo, limit: perSourceLimit, offset: perSourceOffset }));
|
|
248
|
+
if (orRows.length > 0) ctx.orFallbackFired = true;
|
|
245
249
|
for (const r of orRows) results.push(ftsRowToResult(r, { snippet: true }));
|
|
246
250
|
} catch (e) { debugCatch(e, 'searchObservations-or-fallback'); }
|
|
247
251
|
}
|
|
@@ -515,7 +519,7 @@ function searchPrompts(ctx) {
|
|
|
515
519
|
return results;
|
|
516
520
|
}
|
|
517
521
|
|
|
518
|
-
function formatSearchOutput(paginatedResults, args, ftsQuery, totalCount, isCrossSource) {
|
|
522
|
+
function formatSearchOutput(paginatedResults, args, ftsQuery, totalCount, isCrossSource, orFallbackFired = false) {
|
|
519
523
|
if (paginatedResults.length === 0) {
|
|
520
524
|
const hint = [];
|
|
521
525
|
if (args.query && !ftsQuery) {
|
|
@@ -540,7 +544,11 @@ function formatSearchOutput(paginatedResults, args, ftsQuery, totalCount, isCros
|
|
|
540
544
|
// P2-6: empty/omitted query falls through to a "listing recent" path — label it explicitly
|
|
541
545
|
// so callers don't mistake BM25-less results for relevance-ranked ones.
|
|
542
546
|
const qLabel = args.query ? ` for "${args.query}"` : ' (no query — listing recent)';
|
|
543
|
-
|
|
547
|
+
// Surface AND→OR fallback so callers (incl. Claude) know a strict multi-term
|
|
548
|
+
// query actually matched only a subset of the terms. Suppressed when the caller
|
|
549
|
+
// explicitly requested OR semantics — there's no "fallback" in that path.
|
|
550
|
+
const fallbackHint = orFallbackFired && !args.or ? ' (relaxed AND→OR)' : '';
|
|
551
|
+
lines.push(`Found ${countLabel} result(s)${qLabel}${fallbackHint}:${hasMixed ? ' (# observation, S# session, P# prompt)' : ''}\n`);
|
|
544
552
|
|
|
545
553
|
for (const r of paginatedResults) {
|
|
546
554
|
if (r.source === 'obs') {
|
|
@@ -698,7 +706,7 @@ server.registerTool(
|
|
|
698
706
|
// Always apply pagination — single-source results can exceed SQL LIMIT due to expansion (concept co-occurrence, PRF, vector search)
|
|
699
707
|
const paginatedResults = (offset > 0 || results.length > limit) ? results.slice(offset, offset + limit) : results;
|
|
700
708
|
|
|
701
|
-
return formatSearchOutput(paginatedResults, args, ftsQuery, totalBeforePagination, isCrossSource);
|
|
709
|
+
return formatSearchOutput(paginatedResults, args, ftsQuery, totalBeforePagination, isCrossSource, ctx.orFallbackFired === true);
|
|
702
710
|
})
|
|
703
711
|
);
|
|
704
712
|
|