claude-memory-layer 1.0.22 → 1.0.24
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/settings.local.json +11 -0
- package/README.md +2 -0
- package/dist/cli/index.js +87 -17
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +30 -5
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +117 -18
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +7337 -0
- package/dist/hooks/semantic-daemon.js.map +7 -0
- package/dist/hooks/session-end.js +71 -16
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +156 -24
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +101 -18
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +291 -102
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +71 -16
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +71 -16
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +71 -16
- package/dist/services/memory-service.js.map +2 -2
- package/dist/ui/app.js +48 -1
- package/dist/ui/index.html +11 -3
- package/memory/_index.md +1 -0
- package/memory/agent_response/uncategorized/2026-03-04.md +1138 -1
- package/memory/session_summary/uncategorized/2026-03-04.md +31 -0
- package/memory/tool_observation/uncategorized/2026-03-04.md +785 -1
- package/memory/user_prompt/uncategorized/2026-03-04.md +438 -1
- package/package.json +1 -1
- package/scripts/build.ts +2 -1
- package/specs/selective-tool-observation/context.md +100 -0
- package/specs/selective-tool-observation/plan.md +158 -0
- package/specs/selective-tool-observation/spec.md +127 -0
- package/src/cli/index.ts +1 -0
- package/src/core/embedder.ts +15 -4
- package/src/core/sqlite-event-store.ts +16 -0
- package/src/core/turn-state.ts +48 -0
- package/src/core/types.ts +1 -0
- package/src/hooks/post-tool-use.ts +47 -2
- package/src/hooks/semantic-daemon-client.ts +208 -0
- package/src/hooks/semantic-daemon.ts +276 -0
- package/src/hooks/session-start.ts +7 -0
- package/src/hooks/stop.ts +19 -4
- package/src/hooks/user-prompt-submit.ts +48 -40
- package/src/services/memory-service.ts +59 -16
- package/src/services/session-history-importer.ts +18 -0
- package/src/ui/app.js +48 -1
- package/src/ui/index.html +11 -3
|
@@ -1834,6 +1834,21 @@ var SQLiteEventStore = class {
|
|
|
1834
1834
|
[id, eventId, sessionId, score, query.slice(0, 100)]
|
|
1835
1835
|
);
|
|
1836
1836
|
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Get session IDs that have unevaluated retrievals (measured_at IS NULL).
|
|
1839
|
+
* Excludes the current session. Used to backfill sessions that ended without Stop hook.
|
|
1840
|
+
*/
|
|
1841
|
+
async getUnevaluatedSessions(currentSessionId, limit = 5) {
|
|
1842
|
+
await this.initialize();
|
|
1843
|
+
const rows = sqliteAll(
|
|
1844
|
+
this.db,
|
|
1845
|
+
`SELECT DISTINCT session_id FROM memory_helpfulness
|
|
1846
|
+
WHERE measured_at IS NULL AND session_id != ?
|
|
1847
|
+
ORDER BY created_at DESC LIMIT ?`,
|
|
1848
|
+
[currentSessionId, limit]
|
|
1849
|
+
);
|
|
1850
|
+
return rows.map((r) => r.session_id);
|
|
1851
|
+
}
|
|
1837
1852
|
/**
|
|
1838
1853
|
* Evaluate helpfulness for all retrievals in a session
|
|
1839
1854
|
* Called at session end - uses behavioral signals to compute score
|
|
@@ -2621,7 +2636,7 @@ var VectorStore = class {
|
|
|
2621
2636
|
|
|
2622
2637
|
// src/core/embedder.ts
|
|
2623
2638
|
import { pipeline } from "@huggingface/transformers";
|
|
2624
|
-
var Embedder = class {
|
|
2639
|
+
var Embedder = class _Embedder {
|
|
2625
2640
|
pipeline = null;
|
|
2626
2641
|
modelName;
|
|
2627
2642
|
activeModelName;
|
|
@@ -2652,6 +2667,11 @@ var Embedder = class {
|
|
|
2652
2667
|
this.initialized = true;
|
|
2653
2668
|
}
|
|
2654
2669
|
}
|
|
2670
|
+
// ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
|
|
2671
|
+
static MAX_CHARS = 2e3;
|
|
2672
|
+
truncate(text) {
|
|
2673
|
+
return text.length > _Embedder.MAX_CHARS ? text.slice(0, _Embedder.MAX_CHARS) : text;
|
|
2674
|
+
}
|
|
2655
2675
|
/**
|
|
2656
2676
|
* Generate embedding for a single text
|
|
2657
2677
|
*/
|
|
@@ -2660,9 +2680,11 @@ var Embedder = class {
|
|
|
2660
2680
|
if (!this.pipeline) {
|
|
2661
2681
|
throw new Error("Embedding pipeline not initialized");
|
|
2662
2682
|
}
|
|
2663
|
-
const output = await this.pipeline(text, {
|
|
2683
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
2664
2684
|
pooling: "mean",
|
|
2665
|
-
normalize: true
|
|
2685
|
+
normalize: true,
|
|
2686
|
+
truncation: true,
|
|
2687
|
+
max_length: 512
|
|
2666
2688
|
});
|
|
2667
2689
|
const vector = Array.from(output.data);
|
|
2668
2690
|
return {
|
|
@@ -2684,9 +2706,11 @@ var Embedder = class {
|
|
|
2684
2706
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
2685
2707
|
const batch = texts.slice(i, i + batchSize);
|
|
2686
2708
|
for (const text of batch) {
|
|
2687
|
-
const output = await this.pipeline(text, {
|
|
2709
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
2688
2710
|
pooling: "mean",
|
|
2689
|
-
normalize: true
|
|
2711
|
+
normalize: true,
|
|
2712
|
+
truncation: true,
|
|
2713
|
+
max_length: 512
|
|
2690
2714
|
});
|
|
2691
2715
|
const vector = Array.from(output.data);
|
|
2692
2716
|
results.push({
|
|
@@ -6075,6 +6099,7 @@ var MemoryService = class {
|
|
|
6075
6099
|
projectPath = null;
|
|
6076
6100
|
readOnly;
|
|
6077
6101
|
lightweightMode;
|
|
6102
|
+
embeddingOnly;
|
|
6078
6103
|
mdMirror;
|
|
6079
6104
|
storagePath;
|
|
6080
6105
|
constructor(config) {
|
|
@@ -6082,6 +6107,7 @@ var MemoryService = class {
|
|
|
6082
6107
|
this.storagePath = storagePath;
|
|
6083
6108
|
this.readOnly = config.readOnly ?? false;
|
|
6084
6109
|
this.lightweightMode = config.lightweightMode ?? false;
|
|
6110
|
+
this.embeddingOnly = config.embeddingOnly ?? false;
|
|
6085
6111
|
this.mdMirror = new MarkdownMirror2(process.cwd());
|
|
6086
6112
|
if (!this.readOnly && !fs4.existsSync(storagePath)) {
|
|
6087
6113
|
fs4.mkdirSync(storagePath, { recursive: true });
|
|
@@ -6155,19 +6181,21 @@ var MemoryService = class {
|
|
|
6155
6181
|
this.embedder
|
|
6156
6182
|
);
|
|
6157
6183
|
this.vectorWorker.start();
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
this.
|
|
6161
|
-
this.graduation
|
|
6162
|
-
);
|
|
6163
|
-
this.graduationWorker.start();
|
|
6164
|
-
if (this.analyticsStore) {
|
|
6165
|
-
this.syncWorker = new SyncWorker(
|
|
6184
|
+
if (!this.embeddingOnly) {
|
|
6185
|
+
this.retriever.setGraduationPipeline(this.graduation);
|
|
6186
|
+
this.graduationWorker = createGraduationWorker(
|
|
6166
6187
|
this.sqliteStore,
|
|
6167
|
-
this.
|
|
6168
|
-
{ intervalMs: 3e4, batchSize: 500 }
|
|
6188
|
+
this.graduation
|
|
6169
6189
|
);
|
|
6170
|
-
this.
|
|
6190
|
+
this.graduationWorker.start();
|
|
6191
|
+
if (this.analyticsStore) {
|
|
6192
|
+
this.syncWorker = new SyncWorker(
|
|
6193
|
+
this.sqliteStore,
|
|
6194
|
+
this.analyticsStore,
|
|
6195
|
+
{ intervalMs: 3e4, batchSize: 500 }
|
|
6196
|
+
);
|
|
6197
|
+
this.syncWorker.start();
|
|
6198
|
+
}
|
|
6171
6199
|
}
|
|
6172
6200
|
const savedMode = await this.sqliteStore.getEndlessConfig("mode");
|
|
6173
6201
|
if (savedMode === "endless") {
|
|
@@ -6891,6 +6919,19 @@ var MemoryService = class {
|
|
|
6891
6919
|
await this.initialize();
|
|
6892
6920
|
await this.sqliteStore.recordRetrieval(eventId, sessionId, score, query);
|
|
6893
6921
|
}
|
|
6922
|
+
/**
|
|
6923
|
+
* Record a query-level retrieval trace (used by user-prompt-submit hook).
|
|
6924
|
+
* Feeds the retrieval_traces table that powers dashboard stats.
|
|
6925
|
+
*/
|
|
6926
|
+
async recordQueryTrace(input) {
|
|
6927
|
+
await this.initialize();
|
|
6928
|
+
await this.sqliteStore.recordRetrievalTrace({
|
|
6929
|
+
...input,
|
|
6930
|
+
candidateDetails: [],
|
|
6931
|
+
selectedDetails: [],
|
|
6932
|
+
fallbackTrace: []
|
|
6933
|
+
});
|
|
6934
|
+
}
|
|
6894
6935
|
/**
|
|
6895
6936
|
* Evaluate helpfulness of retrievals in a session (called at session end)
|
|
6896
6937
|
*/
|
|
@@ -6898,6 +6939,20 @@ var MemoryService = class {
|
|
|
6898
6939
|
await this.initialize();
|
|
6899
6940
|
await this.sqliteStore.evaluateSessionHelpfulness(sessionId);
|
|
6900
6941
|
}
|
|
6942
|
+
/**
|
|
6943
|
+
* Backfill helpfulness evaluation for sessions that ended without Stop hook.
|
|
6944
|
+
* Call on first turn of a new session to catch missed evaluations.
|
|
6945
|
+
*/
|
|
6946
|
+
async evaluatePendingSessions(currentSessionId) {
|
|
6947
|
+
await this.initialize();
|
|
6948
|
+
const sessions = await this.sqliteStore.getUnevaluatedSessions(currentSessionId, 5);
|
|
6949
|
+
for (const sid of sessions) {
|
|
6950
|
+
try {
|
|
6951
|
+
await this.sqliteStore.evaluateSessionHelpfulness(sid);
|
|
6952
|
+
} catch {
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6901
6956
|
/**
|
|
6902
6957
|
* Get most helpful memories ranked by helpfulness score
|
|
6903
6958
|
*/
|
|
@@ -7450,11 +7505,42 @@ function clearTurnState(sessionId) {
|
|
|
7450
7505
|
// src/hooks/post-tool-use.ts
|
|
7451
7506
|
var DEFAULT_CONFIG6 = {
|
|
7452
7507
|
enabled: true,
|
|
7453
|
-
excludedTools: [
|
|
7508
|
+
excludedTools: [
|
|
7509
|
+
// Trivial meta tools
|
|
7510
|
+
"TodoWrite",
|
|
7511
|
+
"TodoRead",
|
|
7512
|
+
// Reproducible query tools (no storage value)
|
|
7513
|
+
"Read",
|
|
7514
|
+
"Grep",
|
|
7515
|
+
"Glob",
|
|
7516
|
+
"ToolSearch",
|
|
7517
|
+
"WebFetch",
|
|
7518
|
+
"WebSearch",
|
|
7519
|
+
"NotebookRead",
|
|
7520
|
+
// Low-value system tools
|
|
7521
|
+
"Skill",
|
|
7522
|
+
"EnterPlanMode"
|
|
7523
|
+
],
|
|
7524
|
+
minOutputLength: parseInt(process.env.CLAUDE_MEMORY_TOOL_MIN_OUTPUT_LEN || "100"),
|
|
7454
7525
|
maxOutputLength: 1e4,
|
|
7455
7526
|
maxOutputLines: 100,
|
|
7456
7527
|
storeOnlyOnSuccess: false
|
|
7457
7528
|
};
|
|
7529
|
+
var ALWAYS_STORE_TOOLS = /* @__PURE__ */ new Set([
|
|
7530
|
+
"Write",
|
|
7531
|
+
"Edit",
|
|
7532
|
+
"MultiEdit",
|
|
7533
|
+
"Agent",
|
|
7534
|
+
"Task",
|
|
7535
|
+
"ExitPlanMode"
|
|
7536
|
+
]);
|
|
7537
|
+
function hasSignificantOutput(toolName, output, response, minLen) {
|
|
7538
|
+
if (ALWAYS_STORE_TOOLS.has(toolName))
|
|
7539
|
+
return true;
|
|
7540
|
+
if (response?.stderr && response.stderr.trim().length > 0)
|
|
7541
|
+
return true;
|
|
7542
|
+
return output.trim().length >= minLen;
|
|
7543
|
+
}
|
|
7458
7544
|
var DEFAULT_PRIVACY_CONFIG = {
|
|
7459
7545
|
excludePatterns: ["password", "secret", "api_key", "token", "bearer"],
|
|
7460
7546
|
anonymize: false,
|
|
@@ -7491,8 +7577,12 @@ function isToolSuccess(response) {
|
|
|
7491
7577
|
async function main() {
|
|
7492
7578
|
const inputData = await readStdin();
|
|
7493
7579
|
const input = JSON.parse(inputData);
|
|
7494
|
-
const config = DEFAULT_CONFIG6;
|
|
7580
|
+
const config = { ...DEFAULT_CONFIG6 };
|
|
7495
7581
|
const privacyConfig = DEFAULT_PRIVACY_CONFIG;
|
|
7582
|
+
const envBlocklist = process.env.CLAUDE_MEMORY_TOOL_BLOCKLIST;
|
|
7583
|
+
if (envBlocklist !== void 0) {
|
|
7584
|
+
config.excludedTools = envBlocklist.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7585
|
+
}
|
|
7496
7586
|
if (!config.enabled) {
|
|
7497
7587
|
console.log(JSON.stringify({}));
|
|
7498
7588
|
return;
|
|
@@ -7507,6 +7597,15 @@ async function main() {
|
|
|
7507
7597
|
console.log(JSON.stringify({}));
|
|
7508
7598
|
return;
|
|
7509
7599
|
}
|
|
7600
|
+
if (!hasSignificantOutput(
|
|
7601
|
+
input.tool_name,
|
|
7602
|
+
toolOutput,
|
|
7603
|
+
input.tool_response,
|
|
7604
|
+
config.minOutputLength ?? 100
|
|
7605
|
+
)) {
|
|
7606
|
+
console.log(JSON.stringify({}));
|
|
7607
|
+
return;
|
|
7608
|
+
}
|
|
7510
7609
|
try {
|
|
7511
7610
|
const memoryService = getLightweightMemoryService(input.session_id);
|
|
7512
7611
|
const maskedInput = maskSensitiveInput(input.tool_input);
|