claude-mem-lite 2.2.2 → 2.2.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.
- package/dispatch.mjs +29 -18
- package/package.json +1 -1
- package/registry-retriever.mjs +2 -1
- package/registry.mjs +6 -2
- package/server.mjs +3 -1
package/dispatch.mjs
CHANGED
|
@@ -693,7 +693,7 @@ function reRankByKeywords(results, rawKeywords) {
|
|
|
693
693
|
* @returns {object[]} Filtered results with decayed scores
|
|
694
694
|
*/
|
|
695
695
|
function applyAdoptionDecay(results, db) {
|
|
696
|
-
|
|
696
|
+
const decayed = results.map(r => {
|
|
697
697
|
const recs = r.recommend_count || 0;
|
|
698
698
|
const adopts = r.adopt_count || 0;
|
|
699
699
|
if (recs < 10) return r; // Cold start protection
|
|
@@ -721,6 +721,12 @@ function applyAdoptionDecay(results, db) {
|
|
|
721
721
|
}
|
|
722
722
|
return r;
|
|
723
723
|
}).filter(Boolean);
|
|
724
|
+
// Re-sort after decay: decayed zombies must drop in ranking.
|
|
725
|
+
// BM25 scores are negative (more negative = better match), sort ascending.
|
|
726
|
+
if (decayed.some(r => r._decayed)) {
|
|
727
|
+
decayed.sort((a, b) => (a.composite_score ?? a.relevance) - (b.composite_score ?? b.relevance));
|
|
728
|
+
}
|
|
729
|
+
return decayed;
|
|
724
730
|
}
|
|
725
731
|
|
|
726
732
|
/**
|
|
@@ -763,6 +769,24 @@ function passesConfidenceGate(results, signals) {
|
|
|
763
769
|
});
|
|
764
770
|
}
|
|
765
771
|
|
|
772
|
+
// ─── Shared Post-Processing Pipeline ────────────────────────────────────────
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Standard post-processing pipeline for dispatch results.
|
|
776
|
+
* Applies keyword re-ranking, adoption decay, confidence gating, and limit.
|
|
777
|
+
* @param {object[]} results FTS5 results
|
|
778
|
+
* @param {object} signals Context signals
|
|
779
|
+
* @param {object} db Registry database
|
|
780
|
+
* @param {number} [limit=3] Maximum results to return
|
|
781
|
+
* @returns {object[]} Post-processed results
|
|
782
|
+
*/
|
|
783
|
+
function postProcessResults(results, signals, db, limit = 3) {
|
|
784
|
+
results = reRankByKeywords(results, signals.rawKeywords);
|
|
785
|
+
results = applyAdoptionDecay(results, db);
|
|
786
|
+
results = passesConfidenceGate(results, signals);
|
|
787
|
+
return results.slice(0, limit);
|
|
788
|
+
}
|
|
789
|
+
|
|
766
790
|
// ─── Main Dispatch Functions ─────────────────────────────────────────────────
|
|
767
791
|
|
|
768
792
|
/**
|
|
@@ -807,10 +831,7 @@ export async function dispatchOnSessionStart(db, userPrompt, sessionId, { hasHan
|
|
|
807
831
|
}
|
|
808
832
|
}
|
|
809
833
|
|
|
810
|
-
results =
|
|
811
|
-
results = applyAdoptionDecay(results, db);
|
|
812
|
-
results = passesConfidenceGate(results, signals);
|
|
813
|
-
results = results.slice(0, 3);
|
|
834
|
+
results = postProcessResults(results, signals, db);
|
|
814
835
|
|
|
815
836
|
let tier = 2;
|
|
816
837
|
|
|
@@ -827,10 +848,7 @@ export async function dispatchOnSessionStart(db, userPrompt, sessionId, { hasHan
|
|
|
827
848
|
projectDomains,
|
|
828
849
|
});
|
|
829
850
|
if (haikuResults.length > 0) {
|
|
830
|
-
|
|
831
|
-
haikuResults = reRankByKeywords(haikuResults, signals.rawKeywords);
|
|
832
|
-
haikuResults = applyAdoptionDecay(haikuResults, db);
|
|
833
|
-
haikuResults = passesConfidenceGate(haikuResults, signals);
|
|
851
|
+
haikuResults = postProcessResults(haikuResults, signals, db);
|
|
834
852
|
if (haikuResults.length > 0) results = haikuResults;
|
|
835
853
|
}
|
|
836
854
|
}
|
|
@@ -935,13 +953,7 @@ export async function dispatchOnUserPrompt(db, userPrompt, sessionId, { sessionE
|
|
|
935
953
|
}
|
|
936
954
|
}
|
|
937
955
|
|
|
938
|
-
|
|
939
|
-
// match those keywords. "帮我做一下SEO审查" → rawKeywords=["seo"] → SEO audit
|
|
940
|
-
// resources should rank above generic code-review resources.
|
|
941
|
-
results = reRankByKeywords(results, signals.rawKeywords);
|
|
942
|
-
results = applyAdoptionDecay(results, db);
|
|
943
|
-
results = passesConfidenceGate(results, signals);
|
|
944
|
-
results = results.slice(0, 3);
|
|
956
|
+
results = postProcessResults(results, signals, db);
|
|
945
957
|
|
|
946
958
|
if (results.length === 0) return null;
|
|
947
959
|
|
|
@@ -1008,8 +1020,7 @@ export async function dispatchOnPreToolUse(db, event, sessionCtx = {}) {
|
|
|
1008
1020
|
|
|
1009
1021
|
// Tier 2: FTS5 retrieval
|
|
1010
1022
|
let results = retrieveResources(db, query, { limit: 3, projectDomains });
|
|
1011
|
-
results =
|
|
1012
|
-
results = passesConfidenceGate(results, signals);
|
|
1023
|
+
results = postProcessResults(results, signals, db);
|
|
1013
1024
|
if (results.length === 0) return null;
|
|
1014
1025
|
|
|
1015
1026
|
const tier = 2; // Tier 3 disabled for PreToolUse — 2s hook timeout insufficient
|
package/package.json
CHANGED
package/registry-retriever.mjs
CHANGED
|
@@ -196,8 +196,9 @@ export function buildEnhancedQuery(signals) {
|
|
|
196
196
|
// would dilute BM25 precision. Literal matching is sufficient — "seo" matches "seo"
|
|
197
197
|
// directly across name, intent_tags, capability_summary, trigger_patterns.
|
|
198
198
|
if (signals.rawKeywords?.length > 0) {
|
|
199
|
+
const isSafe = t => /^[a-zA-Z0-9]+$/.test(t) || /[\u4e00-\u9fff\u3400-\u4dbf]/.test(t);
|
|
199
200
|
for (const kw of signals.rawKeywords) {
|
|
200
|
-
const safeKw =
|
|
201
|
+
const safeKw = isSafe(kw) ? kw : `"${kw.replace(/"/g, '""')}"`;
|
|
201
202
|
parts.push(`intent_tags:${safeKw}`);
|
|
202
203
|
parts.push(safeKw);
|
|
203
204
|
}
|
package/registry.mjs
CHANGED
|
@@ -104,7 +104,7 @@ const INVOCATIONS_SCHEMA = `
|
|
|
104
104
|
tier INTEGER CHECK(tier IN (1,2,3)),
|
|
105
105
|
recommended INTEGER DEFAULT 1,
|
|
106
106
|
adopted INTEGER DEFAULT 0,
|
|
107
|
-
outcome TEXT CHECK(outcome IN ('success','partial','failure','skipped','ignored'
|
|
107
|
+
outcome TEXT CHECK(outcome IN ('success','partial','failure','skipped','ignored') OR outcome IS NULL),
|
|
108
108
|
score REAL,
|
|
109
109
|
created_at TEXT DEFAULT (datetime('now'))
|
|
110
110
|
);
|
|
@@ -161,7 +161,7 @@ export function ensureRegistryDb(dbPath) {
|
|
|
161
161
|
if (!cols.some(c => c.name === 'invocation_name')) {
|
|
162
162
|
db.exec("ALTER TABLE resources ADD COLUMN invocation_name TEXT DEFAULT ''");
|
|
163
163
|
}
|
|
164
|
-
} catch {}
|
|
164
|
+
} catch (e) { debugCatch(e, 'invocation_name-migration'); }
|
|
165
165
|
|
|
166
166
|
// FTS5 + triggers: only create if not exists
|
|
167
167
|
const hasFts = db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='resources_fts'`).get();
|
|
@@ -215,6 +215,10 @@ export function ensureRegistryDb(dbPath) {
|
|
|
215
215
|
return db;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
// ─── Exported Schema (for test-helpers.mjs) ─────────────────────────────────
|
|
219
|
+
|
|
220
|
+
export { RESOURCES_SCHEMA, FTS5_SCHEMA, TRIGGERS_SCHEMA, INVOCATIONS_SCHEMA, PREINSTALLED_SCHEMA };
|
|
221
|
+
|
|
218
222
|
// ─── Resource CRUD ───────────────────────────────────────────────────────────
|
|
219
223
|
|
|
220
224
|
const UPSERT_SQL = `
|
package/server.mjs
CHANGED
|
@@ -1001,7 +1001,9 @@ const idleTimer = setInterval(() => {
|
|
|
1001
1001
|
const thirtyDaysAgo = Date.now() - 30 * 86400000;
|
|
1002
1002
|
|
|
1003
1003
|
db.transaction(() => {
|
|
1004
|
-
// Delete old low-quality observations (importance<=1, never accessed, 30+ days)
|
|
1004
|
+
// Delete old low-quality observations (importance<=1, never accessed, 30+ days).
|
|
1005
|
+
// NOTE: no project filter — MCP server is global, operates across all projects.
|
|
1006
|
+
// This is intentionally broader than hook.mjs auto-compress (which scopes to current project).
|
|
1005
1007
|
const deleted = db.prepare(`
|
|
1006
1008
|
DELETE FROM observations
|
|
1007
1009
|
WHERE importance <= 1 AND COALESCE(access_count, 0) = 0
|