claude-memory-layer 1.0.23 → 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 +85 -17
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +28 -5
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +115 -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 +69 -16
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +154 -24
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +99 -18
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +289 -102
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/server/api/index.js +69 -16
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +69 -16
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +69 -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 +1098 -1
- package/memory/session_summary/uncategorized/2026-03-04.md +31 -0
- package/memory/tool_observation/uncategorized/2026-03-04.md +733 -1
- package/memory/user_prompt/uncategorized/2026-03-04.md +371 -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 +13 -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,10 +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
2699
|
normalize: true,
|
|
2680
|
-
truncation: true
|
|
2700
|
+
truncation: true,
|
|
2701
|
+
max_length: 512
|
|
2681
2702
|
});
|
|
2682
2703
|
const vector = Array.from(output.data);
|
|
2683
2704
|
return {
|
|
@@ -2699,10 +2720,11 @@ var Embedder = class {
|
|
|
2699
2720
|
for (let i = 0; i < texts.length; i += batchSize) {
|
|
2700
2721
|
const batch = texts.slice(i, i + batchSize);
|
|
2701
2722
|
for (const text of batch) {
|
|
2702
|
-
const output = await this.pipeline(text, {
|
|
2723
|
+
const output = await this.pipeline(this.truncate(text), {
|
|
2703
2724
|
pooling: "mean",
|
|
2704
2725
|
normalize: true,
|
|
2705
|
-
truncation: true
|
|
2726
|
+
truncation: true,
|
|
2727
|
+
max_length: 512
|
|
2706
2728
|
});
|
|
2707
2729
|
const vector = Array.from(output.data);
|
|
2708
2730
|
results.push({
|
|
@@ -5993,6 +6015,7 @@ var MemoryService = class {
|
|
|
5993
6015
|
projectPath = null;
|
|
5994
6016
|
readOnly;
|
|
5995
6017
|
lightweightMode;
|
|
6018
|
+
embeddingOnly;
|
|
5996
6019
|
mdMirror;
|
|
5997
6020
|
storagePath;
|
|
5998
6021
|
constructor(config) {
|
|
@@ -6000,6 +6023,7 @@ var MemoryService = class {
|
|
|
6000
6023
|
this.storagePath = storagePath;
|
|
6001
6024
|
this.readOnly = config.readOnly ?? false;
|
|
6002
6025
|
this.lightweightMode = config.lightweightMode ?? false;
|
|
6026
|
+
this.embeddingOnly = config.embeddingOnly ?? false;
|
|
6003
6027
|
this.mdMirror = new MarkdownMirror2(process.cwd());
|
|
6004
6028
|
if (!this.readOnly && !fs4.existsSync(storagePath)) {
|
|
6005
6029
|
fs4.mkdirSync(storagePath, { recursive: true });
|
|
@@ -6073,19 +6097,21 @@ var MemoryService = class {
|
|
|
6073
6097
|
this.embedder
|
|
6074
6098
|
);
|
|
6075
6099
|
this.vectorWorker.start();
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
this.
|
|
6079
|
-
this.graduation
|
|
6080
|
-
);
|
|
6081
|
-
this.graduationWorker.start();
|
|
6082
|
-
if (this.analyticsStore) {
|
|
6083
|
-
this.syncWorker = new SyncWorker(
|
|
6100
|
+
if (!this.embeddingOnly) {
|
|
6101
|
+
this.retriever.setGraduationPipeline(this.graduation);
|
|
6102
|
+
this.graduationWorker = createGraduationWorker(
|
|
6084
6103
|
this.sqliteStore,
|
|
6085
|
-
this.
|
|
6086
|
-
{ intervalMs: 3e4, batchSize: 500 }
|
|
6104
|
+
this.graduation
|
|
6087
6105
|
);
|
|
6088
|
-
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
|
+
}
|
|
6089
6115
|
}
|
|
6090
6116
|
const savedMode = await this.sqliteStore.getEndlessConfig("mode");
|
|
6091
6117
|
if (savedMode === "endless") {
|
|
@@ -6809,6 +6835,19 @@ var MemoryService = class {
|
|
|
6809
6835
|
await this.initialize();
|
|
6810
6836
|
await this.sqliteStore.recordRetrieval(eventId, sessionId, score, query);
|
|
6811
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
|
+
}
|
|
6812
6851
|
/**
|
|
6813
6852
|
* Evaluate helpfulness of retrievals in a session (called at session end)
|
|
6814
6853
|
*/
|
|
@@ -6816,6 +6855,20 @@ var MemoryService = class {
|
|
|
6816
6855
|
await this.initialize();
|
|
6817
6856
|
await this.sqliteStore.evaluateSessionHelpfulness(sessionId);
|
|
6818
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
|
+
}
|
|
6819
6872
|
/**
|
|
6820
6873
|
* Get most helpful memories ranked by helpfulness score
|
|
6821
6874
|
*/
|
|
@@ -7148,6 +7201,16 @@ import * as path4 from "path";
|
|
|
7148
7201
|
import * as os2 from "os";
|
|
7149
7202
|
import * as readline from "readline";
|
|
7150
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
|
+
}
|
|
7151
7214
|
function classifyEntry(entry) {
|
|
7152
7215
|
if (entry.type !== "user" && entry.type !== "assistant") {
|
|
7153
7216
|
return "skip";
|
|
@@ -7324,6 +7387,10 @@ var SessionHistoryImporter = class {
|
|
|
7324
7387
|
const content = this.extractContent(entry);
|
|
7325
7388
|
if (!content)
|
|
7326
7389
|
continue;
|
|
7390
|
+
if (!isWorthStoringPrompt(content)) {
|
|
7391
|
+
result.skippedDuplicates++;
|
|
7392
|
+
continue;
|
|
7393
|
+
}
|
|
7327
7394
|
currentTurnId = randomUUID9();
|
|
7328
7395
|
const appendResult = await this.memoryService.storeUserPrompt(
|
|
7329
7396
|
sessionId,
|
|
@@ -9711,7 +9778,7 @@ function getHooksConfig(pluginPath) {
|
|
|
9711
9778
|
};
|
|
9712
9779
|
}
|
|
9713
9780
|
var program = new Command();
|
|
9714
|
-
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");
|
|
9715
9782
|
program.command("install").description("Install hooks into Claude Code settings").option("--path <path>", "Custom plugin path (defaults to auto-detect)").action(async (options) => {
|
|
9716
9783
|
try {
|
|
9717
9784
|
const pluginPath = options.path || getPluginPath();
|
|
@@ -9912,6 +9979,7 @@ program.command("process").description("Process pending embeddings").option("-p,
|
|
|
9912
9979
|
const projectPath = options.project || process.cwd();
|
|
9913
9980
|
const service = getMemoryServiceForProject(projectPath);
|
|
9914
9981
|
try {
|
|
9982
|
+
await service.initialize();
|
|
9915
9983
|
console.log("\u23F3 Processing pending embeddings...");
|
|
9916
9984
|
const count = await service.processPendingEmbeddings();
|
|
9917
9985
|
console.log(`\u2705 Processed ${count} embeddings`);
|