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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(curl -s http://localhost:37777/api/stats | python3 -m json.tool 2>/dev/null || curl -s http://localhost:37777/api/stats)",
|
|
5
|
+
"Bash(curl -s http://localhost:37777/api/projects | python3 -m json.tool 2>/dev/null | head -60)",
|
|
6
|
+
"Bash(curl -s \"http://localhost:37777/api/retrievals?project=f4d5c120&limit=10\" | python3 -m json.tool 2>/dev/null | head -60)",
|
|
7
|
+
"Bash(curl -s \"http://localhost:37777/api/stats/retrieval-traces?project=f4d5c120&limit=5\" | python3 -m json.tool 2>/dev/null | head -30)",
|
|
8
|
+
"Bash(curl -s \"https://be2f-222-112-203-51.ngrok-free.app/api/health?project=f4d5c120\" | python3 -m json.tool)"
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
}
|
package/README.md
CHANGED
|
@@ -72,6 +72,8 @@ npx claude-memory-layer search "배포 이슈"
|
|
|
72
72
|
- `GET /api/health` (outbox pending/failed 포함 상세 헬스)
|
|
73
73
|
- `GET /api/stats/retrieval-traces` (검색→컨텍스트 채택 추적)
|
|
74
74
|
- 주입 임계값 튜닝(환경변수):
|
|
75
|
+
- `CLAUDE_MEMORY_RETRIEVAL_MODE` (기본 `hybrid`, `keyword`/`hybrid`/`semantic`)
|
|
76
|
+
- `CLAUDE_MEMORY_SEMANTIC_DAEMON_IDLE_MS` (기본 `600000`, semantic daemon 유휴 종료 시간)
|
|
75
77
|
- `CLAUDE_MEMORY_MIN_SCORE` (기본 0.4)
|
|
76
78
|
- `CLAUDE_MEMORY_FALLBACK_MIN_SCORE` (기본 0.3, 결과 0건일 때 재시도)
|
|
77
79
|
|
package/dist/cli/index.js
CHANGED
|
@@ -1848,6 +1848,21 @@ var SQLiteEventStore = class {
|
|
|
1848
1848
|
[id, eventId, sessionId, score, query.slice(0, 100)]
|
|
1849
1849
|
);
|
|
1850
1850
|
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Get session IDs that have unevaluated retrievals (measured_at IS NULL).
|
|
1853
|
+
* Excludes the current session. Used to backfill sessions that ended without Stop hook.
|
|
1854
|
+
*/
|
|
1855
|
+
async getUnevaluatedSessions(currentSessionId, limit = 5) {
|
|
1856
|
+
await this.initialize();
|
|
1857
|
+
const rows = sqliteAll(
|
|
1858
|
+
this.db,
|
|
1859
|
+
`SELECT DISTINCT session_id FROM memory_helpfulness
|
|
1860
|
+
WHERE measured_at IS NULL AND session_id != ?
|
|
1861
|
+
ORDER BY created_at DESC LIMIT ?`,
|
|
1862
|
+
[currentSessionId, limit]
|
|
1863
|
+
);
|
|
1864
|
+
return rows.map((r) => r.session_id);
|
|
1865
|
+
}
|
|
1851
1866
|
/**
|
|
1852
1867
|
* Evaluate helpfulness for all retrievals in a session
|
|
1853
1868
|
* Called at session end - uses behavioral signals to compute score
|
|
@@ -2635,7 +2650,7 @@ var VectorStore = class {
|
|
|
2635
2650
|
|
|
2636
2651
|
// src/core/embedder.ts
|
|
2637
2652
|
import { pipeline } from "@huggingface/transformers";
|
|
2638
|
-
var Embedder = class {
|
|
2653
|
+
var Embedder = class _Embedder {
|
|
2639
2654
|
pipeline = null;
|
|
2640
2655
|
modelName;
|
|
2641
2656
|
activeModelName;
|
|
@@ -2666,6 +2681,11 @@ var Embedder = class {
|
|
|
2666
2681
|
this.initialized = true;
|
|
2667
2682
|
}
|
|
2668
2683
|
}
|
|
2684
|
+
// ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
|
|
2685
|
+
static MAX_CHARS = 2e3;
|
|
2686
|
+
truncate(text) {
|
|
2687
|
+
return text.length > _Embedder.MAX_CHARS ? text.slice(0, _Embedder.MAX_CHARS) : text;
|
|
2688
|
+
}
|
|
2669
2689
|
/**
|
|
2670
2690
|
* Generate embedding for a single text
|
|
2671
2691
|
*/
|
|
@@ -2674,9 +2694,11 @@ var Embedder = class {
|
|
|
2674
2694
|
if (!this.pipeline) {
|
|
2675
2695
|
throw new Error("Embedding pipeline not initialized");
|
|
2676
2696
|
}
|
|
2677
|
-
const output = await this.pipeline(text, {
|
|
2697
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
2678
2698
|
pooling: "mean",
|
|
2679
|
-
normalize: true
|
|
2699
|
+
normalize: true,
|
|
2700
|
+
truncation: true,
|
|
2701
|
+
max_length: 512
|
|
2680
2702
|
});
|
|
2681
2703
|
const vector = Array.from(output.data);
|
|
2682
2704
|
return {
|
|
@@ -2698,9 +2720,11 @@ var Embedder = class {
|
|
|
2698
2720
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
2699
2721
|
const batch = texts.slice(i, i + batchSize);
|
|
2700
2722
|
for (const text of batch) {
|
|
2701
|
-
const output = await this.pipeline(text, {
|
|
2723
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
2702
2724
|
pooling: "mean",
|
|
2703
|
-
normalize: true
|
|
2725
|
+
normalize: true,
|
|
2726
|
+
truncation: true,
|
|
2727
|
+
max_length: 512
|
|
2704
2728
|
});
|
|
2705
2729
|
const vector = Array.from(output.data);
|
|
2706
2730
|
results.push({
|
|
@@ -5991,6 +6015,7 @@ var MemoryService = class {
|
|
|
5991
6015
|
projectPath = null;
|
|
5992
6016
|
readOnly;
|
|
5993
6017
|
lightweightMode;
|
|
6018
|
+
embeddingOnly;
|
|
5994
6019
|
mdMirror;
|
|
5995
6020
|
storagePath;
|
|
5996
6021
|
constructor(config) {
|
|
@@ -5998,6 +6023,7 @@ var MemoryService = class {
|
|
|
5998
6023
|
this.storagePath = storagePath;
|
|
5999
6024
|
this.readOnly = config.readOnly ?? false;
|
|
6000
6025
|
this.lightweightMode = config.lightweightMode ?? false;
|
|
6026
|
+
this.embeddingOnly = config.embeddingOnly ?? false;
|
|
6001
6027
|
this.mdMirror = new MarkdownMirror2(process.cwd());
|
|
6002
6028
|
if (!this.readOnly && !fs4.existsSync(storagePath)) {
|
|
6003
6029
|
fs4.mkdirSync(storagePath, { recursive: true });
|
|
@@ -6071,19 +6097,21 @@ var MemoryService = class {
|
|
|
6071
6097
|
this.embedder
|
|
6072
6098
|
);
|
|
6073
6099
|
this.vectorWorker.start();
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
this.
|
|
6077
|
-
this.graduation
|
|
6078
|
-
);
|
|
6079
|
-
this.graduationWorker.start();
|
|
6080
|
-
if (this.analyticsStore) {
|
|
6081
|
-
this.syncWorker = new SyncWorker(
|
|
6100
|
+
if (!this.embeddingOnly) {
|
|
6101
|
+
this.retriever.setGraduationPipeline(this.graduation);
|
|
6102
|
+
this.graduationWorker = createGraduationWorker(
|
|
6082
6103
|
this.sqliteStore,
|
|
6083
|
-
this.
|
|
6084
|
-
{ intervalMs: 3e4, batchSize: 500 }
|
|
6104
|
+
this.graduation
|
|
6085
6105
|
);
|
|
6086
|
-
this.
|
|
6106
|
+
this.graduationWorker.start();
|
|
6107
|
+
if (this.analyticsStore) {
|
|
6108
|
+
this.syncWorker = new SyncWorker(
|
|
6109
|
+
this.sqliteStore,
|
|
6110
|
+
this.analyticsStore,
|
|
6111
|
+
{ intervalMs: 3e4, batchSize: 500 }
|
|
6112
|
+
);
|
|
6113
|
+
this.syncWorker.start();
|
|
6114
|
+
}
|
|
6087
6115
|
}
|
|
6088
6116
|
const savedMode = await this.sqliteStore.getEndlessConfig("mode");
|
|
6089
6117
|
if (savedMode === "endless") {
|
|
@@ -6807,6 +6835,19 @@ var MemoryService = class {
|
|
|
6807
6835
|
await this.initialize();
|
|
6808
6836
|
await this.sqliteStore.recordRetrieval(eventId, sessionId, score, query);
|
|
6809
6837
|
}
|
|
6838
|
+
/**
|
|
6839
|
+
* Record a query-level retrieval trace (used by user-prompt-submit hook).
|
|
6840
|
+
* Feeds the retrieval_traces table that powers dashboard stats.
|
|
6841
|
+
*/
|
|
6842
|
+
async recordQueryTrace(input) {
|
|
6843
|
+
await this.initialize();
|
|
6844
|
+
await this.sqliteStore.recordRetrievalTrace({
|
|
6845
|
+
...input,
|
|
6846
|
+
candidateDetails: [],
|
|
6847
|
+
selectedDetails: [],
|
|
6848
|
+
fallbackTrace: []
|
|
6849
|
+
});
|
|
6850
|
+
}
|
|
6810
6851
|
/**
|
|
6811
6852
|
* Evaluate helpfulness of retrievals in a session (called at session end)
|
|
6812
6853
|
*/
|
|
@@ -6814,6 +6855,20 @@ var MemoryService = class {
|
|
|
6814
6855
|
await this.initialize();
|
|
6815
6856
|
await this.sqliteStore.evaluateSessionHelpfulness(sessionId);
|
|
6816
6857
|
}
|
|
6858
|
+
/**
|
|
6859
|
+
* Backfill helpfulness evaluation for sessions that ended without Stop hook.
|
|
6860
|
+
* Call on first turn of a new session to catch missed evaluations.
|
|
6861
|
+
*/
|
|
6862
|
+
async evaluatePendingSessions(currentSessionId) {
|
|
6863
|
+
await this.initialize();
|
|
6864
|
+
const sessions = await this.sqliteStore.getUnevaluatedSessions(currentSessionId, 5);
|
|
6865
|
+
for (const sid of sessions) {
|
|
6866
|
+
try {
|
|
6867
|
+
await this.sqliteStore.evaluateSessionHelpfulness(sid);
|
|
6868
|
+
} catch {
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
}
|
|
6817
6872
|
/**
|
|
6818
6873
|
* Get most helpful memories ranked by helpfulness score
|
|
6819
6874
|
*/
|
|
@@ -7146,6 +7201,16 @@ import * as path4 from "path";
|
|
|
7146
7201
|
import * as os2 from "os";
|
|
7147
7202
|
import * as readline from "readline";
|
|
7148
7203
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
7204
|
+
function isWorthStoringPrompt(content) {
|
|
7205
|
+
const trimmed = content.trim();
|
|
7206
|
+
if (trimmed.startsWith("/"))
|
|
7207
|
+
return false;
|
|
7208
|
+
if (trimmed.length < 15)
|
|
7209
|
+
return false;
|
|
7210
|
+
if (!/[a-zA-Z가-힣]{2,}/.test(trimmed))
|
|
7211
|
+
return false;
|
|
7212
|
+
return true;
|
|
7213
|
+
}
|
|
7149
7214
|
function classifyEntry(entry) {
|
|
7150
7215
|
if (entry.type !== "user" && entry.type !== "assistant") {
|
|
7151
7216
|
return "skip";
|
|
@@ -7322,6 +7387,10 @@ var SessionHistoryImporter = class {
|
|
|
7322
7387
|
const content = this.extractContent(entry);
|
|
7323
7388
|
if (!content)
|
|
7324
7389
|
continue;
|
|
7390
|
+
if (!isWorthStoringPrompt(content)) {
|
|
7391
|
+
result.skippedDuplicates++;
|
|
7392
|
+
continue;
|
|
7393
|
+
}
|
|
7325
7394
|
currentTurnId = randomUUID9();
|
|
7326
7395
|
const appendResult = await this.memoryService.storeUserPrompt(
|
|
7327
7396
|
sessionId,
|
|
@@ -9709,7 +9778,7 @@ function getHooksConfig(pluginPath) {
|
|
|
9709
9778
|
};
|
|
9710
9779
|
}
|
|
9711
9780
|
var program = new Command();
|
|
9712
|
-
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.
|
|
9781
|
+
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.24");
|
|
9713
9782
|
program.command("install").description("Install hooks into Claude Code settings").option("--path <path>", "Custom plugin path (defaults to auto-detect)").action(async (options) => {
|
|
9714
9783
|
try {
|
|
9715
9784
|
const pluginPath = options.path || getPluginPath();
|
|
@@ -9910,6 +9979,7 @@ program.command("process").description("Process pending embeddings").option("-p,
|
|
|
9910
9979
|
const projectPath = options.project || process.cwd();
|
|
9911
9980
|
const service = getMemoryServiceForProject(projectPath);
|
|
9912
9981
|
try {
|
|
9982
|
+
await service.initialize();
|
|
9913
9983
|
console.log("\u23F3 Processing pending embeddings...");
|
|
9914
9984
|
const count = await service.processPendingEmbeddings();
|
|
9915
9985
|
console.log(`\u2705 Processed ${count} embeddings`);
|