claude-mem-lite 3.87.0 β 3.89.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/README.md +1 -1
- package/cli-path.mjs +5 -1
- package/hook-context.mjs +88 -24
- package/hook-llm.mjs +10 -2
- package/hook-memory.mjs +23 -13
- package/hook.mjs +45 -4
- package/lib/citation-tracker.mjs +176 -104
- package/lib/deferred-work.mjs +81 -8
- package/lib/events-injection.mjs +12 -1
- package/lib/injected-ids.mjs +17 -0
- package/lib/maintain-core.mjs +4 -2
- package/lib/native-binding-hint.mjs +5 -2
- package/lib/save-observation.mjs +231 -12
- package/mem-cli.mjs +53 -17
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/scoring-sql.mjs +11 -1
- package/scripts/pre-tool-recall.js +32 -6
- package/server.mjs +19 -9
- package/tool-schemas.mjs +11 -4
package/server.mjs
CHANGED
|
@@ -50,7 +50,7 @@ import { ensureRegistryDb, collectRegistryStats, listResourcesRanked, formatRegi
|
|
|
50
50
|
import { IMPORT_STRING_FIELDS, importResource, removeResource, reindexResources } from './lib/registry-core.mjs';
|
|
51
51
|
import { searchResources } from './registry-retriever.mjs';
|
|
52
52
|
import { probeOtherSources as probeIdSources, bucketIdTokens, splitDeferredTokens } from './lib/id-routing.mjs';
|
|
53
|
-
import { saveObservation } from './lib/save-observation.mjs';
|
|
53
|
+
import { saveObservation, formatSupersedeSkipped, formatSupersededNote } from './lib/save-observation.mjs';
|
|
54
54
|
import { applyObsUpdate } from './lib/observation-write.mjs';
|
|
55
55
|
import { EXPORT_COLUMNS_SQL } from './lib/export-columns.mjs';
|
|
56
56
|
import { liveObsFilterSql } from './lib/inject-search-core.mjs';
|
|
@@ -58,7 +58,7 @@ import { recallByFile } from './lib/recall-core.mjs';
|
|
|
58
58
|
import { fetchRecent } from './lib/recent-core.mjs';
|
|
59
59
|
import { AUTO_MERGE_THRESHOLD } from './lib/dedup-constants.mjs';
|
|
60
60
|
import {
|
|
61
|
-
insertDeferred, listOpenWithOrdinal, dropDeferred,
|
|
61
|
+
insertDeferred, listOpenWithOrdinal, dropDeferred, formatDropReasonHint,
|
|
62
62
|
resolveDeferredIds, closeDeferredItems,
|
|
63
63
|
getDeferredByIds, formatDeferredDetail,
|
|
64
64
|
searchDeferredWork, formatDeferredSearchTrailer,
|
|
@@ -853,7 +853,9 @@ server.registerTool(
|
|
|
853
853
|
// Resolve INSIDE tx + after dedup check so duplicate replays don't throw on
|
|
854
854
|
// already-closed items. Mirrors mem-cli.mjs cmdSave shape.
|
|
855
855
|
if (args.closes_deferred && args.closes_deferred.length > 0) {
|
|
856
|
-
|
|
856
|
+
// D#195: 'dropped' is closable by the close verb (kept in sync with
|
|
857
|
+
// mem-cli.mjs cmdSave β same policy, both faces).
|
|
858
|
+
closesIds = resolveDeferredIds(db, project, args.closes_deferred, { allowStatuses: ['open', 'dropped'] });
|
|
857
859
|
closeDeferredItems(db, closesIds, r.id);
|
|
858
860
|
}
|
|
859
861
|
return r;
|
|
@@ -869,22 +871,26 @@ server.registerTool(
|
|
|
869
871
|
}
|
|
870
872
|
|
|
871
873
|
if (result.kind === 'duplicate') {
|
|
872
|
-
|
|
874
|
+
// D#201: this branch returns before the note below, so it renders its own.
|
|
875
|
+
const dupSkip = formatSupersedeSkipped(result.supersedeSkipped);
|
|
876
|
+
const dupText = `Skipped: similar to existing #${result.existingId} in project "${project}". Use mem_get(ids=[${result.existingId}]) to review.`;
|
|
877
|
+
return { content: [{ type: 'text', text: dupSkip ? `${dupText}\n${dupSkip}` : dupText }] };
|
|
873
878
|
}
|
|
874
879
|
|
|
875
880
|
const lessonNote = result.lessonCaptured ? ` π‘lesson captured` : '';
|
|
876
881
|
const closedNote = closesIds && closesIds.length > 0
|
|
877
882
|
? ` Closed deferred: ${closesIds.map(i => `D#${i}`).join(', ')}.`
|
|
878
883
|
: '';
|
|
879
|
-
const supersededNote = result
|
|
880
|
-
? ` Superseded: ${result.supersededIds.map(i => `#${i}`).join(', ')}.`
|
|
881
|
-
: '';
|
|
884
|
+
const supersededNote = formatSupersededNote(result);
|
|
882
885
|
const nudge = buildLessonNudge({ type: result.type, id: result.id, lessonCaptured: result.lessonCaptured, surface: 'mcp' });
|
|
883
886
|
// G1+G2: detached backfill worker (lesson for obligated types + aliases for
|
|
884
887
|
// every save) β fill-only-empty, so an agent acting on the nudge still wins.
|
|
885
888
|
const enrichNote = shouldQueueSaveEnrich(result) && queueSaveEnrich(result.id)
|
|
886
889
|
? ' (background enrichment queued)' : '';
|
|
887
|
-
|
|
890
|
+
// D#201: on its own line rather than inside the success sentence.
|
|
891
|
+
const skipNote = formatSupersedeSkipped(result.supersedeSkipped);
|
|
892
|
+
const savedText = `Saved as observation #${result.id} [${result.type}] in project "${project}".${lessonNote}${closedNote}${supersededNote}${enrichNote}${nudge}`;
|
|
893
|
+
return { content: [{ type: 'text', text: skipNote ? `${savedText}\n${skipNote}` : savedText }] };
|
|
888
894
|
})
|
|
889
895
|
);
|
|
890
896
|
|
|
@@ -960,7 +966,11 @@ server.registerTool(
|
|
|
960
966
|
if (r.changed === 0) {
|
|
961
967
|
return { content: [{ type: 'text', text: `D#${realId} was not in 'open' status β drop is a no-op.` }] };
|
|
962
968
|
}
|
|
963
|
-
|
|
969
|
+
// D#195 (c): same advisory as the CLI's `defer drop`, so an agent reaching
|
|
970
|
+
// this through MCP gets the same steer toward `mem_save(closes_deferred)`.
|
|
971
|
+
const hint = formatDropReasonHint(args.reason);
|
|
972
|
+
const dropText = `Dropped D#${realId} in project "${project}". Reason: ${args.reason}`;
|
|
973
|
+
return { content: [{ type: 'text', text: hint ? `${dropText}\n${hint}` : dropText }] };
|
|
964
974
|
})
|
|
965
975
|
);
|
|
966
976
|
|
package/tool-schemas.mjs
CHANGED
|
@@ -191,13 +191,20 @@ const coerceDeferredTokens = z.preprocess(
|
|
|
191
191
|
])).min(1).max(20)
|
|
192
192
|
);
|
|
193
193
|
|
|
194
|
-
// Coerce supersedes input β
|
|
195
|
-
//
|
|
194
|
+
// Coerce supersedes input β positive observation ids, plus `E#<n>` for an EVENT row
|
|
195
|
+
// (D#205), which is the prefix those rows are rendered with in the injected lessons
|
|
196
|
+
// block. Numeric strings are accepted because some MCP bridges JSON-stringify ints;
|
|
197
|
+
// `E#<n>` is kept as a STRING and split by `splitSupersedeTokens`, since coercing it to
|
|
198
|
+
// a number here would lose the only thing that says which table it names.
|
|
199
|
+
// Empty/other shapes reject.
|
|
196
200
|
const coerceSupersedes = z.preprocess(
|
|
197
201
|
(v) => (Array.isArray(v)
|
|
198
202
|
? v.map(x => (typeof x === 'string' && /^\d+$/.test(x.trim()) ? parseInt(x.trim(), 10) : x))
|
|
199
203
|
: v),
|
|
200
|
-
z.array(z.
|
|
204
|
+
z.array(z.union([
|
|
205
|
+
z.number().int().positive(),
|
|
206
|
+
z.string().regex(/^[Ee]#?\d+$/, 'expected a positive observation id or E#<n> for an event'),
|
|
207
|
+
])).min(1).max(20)
|
|
201
208
|
);
|
|
202
209
|
|
|
203
210
|
export const memSaveSchema = {
|
|
@@ -216,7 +223,7 @@ export const memSaveSchema = {
|
|
|
216
223
|
files: coerceStringArray.optional().describe('File paths associated with this observation. Stored in the `files_modified` column and rendered as `files` β passing a path here does not assert the file was edited; a file you only read belongs here too'),
|
|
217
224
|
lesson_learned: z.string().max(500).optional().describe('Key lesson or takeaway, β€500 chars (for bugfix: root cause & fix; for decision: rationale)'),
|
|
218
225
|
closes_deferred: coerceDeferredTokens.optional().describe('Close one or more deferred_work items in the same project. Mixed array: bare integer = ordinal-within-project, "D#<n>" string = raw id. Transactional with the obs insert β a single invalid id rolls back the whole save.'),
|
|
219
|
-
supersedes: coerceSupersedes.optional().describe('
|
|
226
|
+
supersedes: coerceSupersedes.optional().describe('Ids (same project) that this save overturns: a bare number for an observation, or E#<n> for an event β the same prefix events are shown with in the injected lessons block, so you can retire one by typing back what you read. They are marked superseded (dropped from live search); observations are also linked to the new row via superseded_by, events are not, because that column can only reference another event. Use ONLY when this genuinely replaces a prior conclusion; do NOT use for merely-related or updated-but-still-valid memories.'),
|
|
220
227
|
};
|
|
221
228
|
|
|
222
229
|
export const memStatsSchema = {
|