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.
Files changed (51) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/README.md +2 -0
  3. package/dist/cli/index.js +85 -17
  4. package/dist/cli/index.js.map +2 -2
  5. package/dist/core/index.js +28 -5
  6. package/dist/core/index.js.map +2 -2
  7. package/dist/hooks/post-tool-use.js +115 -18
  8. package/dist/hooks/post-tool-use.js.map +2 -2
  9. package/dist/hooks/semantic-daemon.js +7337 -0
  10. package/dist/hooks/semantic-daemon.js.map +7 -0
  11. package/dist/hooks/session-end.js +69 -16
  12. package/dist/hooks/session-end.js.map +2 -2
  13. package/dist/hooks/session-start.js +154 -24
  14. package/dist/hooks/session-start.js.map +4 -4
  15. package/dist/hooks/stop.js +99 -18
  16. package/dist/hooks/stop.js.map +2 -2
  17. package/dist/hooks/user-prompt-submit.js +289 -102
  18. package/dist/hooks/user-prompt-submit.js.map +4 -4
  19. package/dist/server/api/index.js +69 -16
  20. package/dist/server/api/index.js.map +2 -2
  21. package/dist/server/index.js +69 -16
  22. package/dist/server/index.js.map +2 -2
  23. package/dist/services/memory-service.js +69 -16
  24. package/dist/services/memory-service.js.map +2 -2
  25. package/dist/ui/app.js +48 -1
  26. package/dist/ui/index.html +11 -3
  27. package/memory/_index.md +1 -0
  28. package/memory/agent_response/uncategorized/2026-03-04.md +1098 -1
  29. package/memory/session_summary/uncategorized/2026-03-04.md +31 -0
  30. package/memory/tool_observation/uncategorized/2026-03-04.md +733 -1
  31. package/memory/user_prompt/uncategorized/2026-03-04.md +371 -1
  32. package/package.json +1 -1
  33. package/scripts/build.ts +2 -1
  34. package/specs/selective-tool-observation/context.md +100 -0
  35. package/specs/selective-tool-observation/plan.md +158 -0
  36. package/specs/selective-tool-observation/spec.md +127 -0
  37. package/src/cli/index.ts +1 -0
  38. package/src/core/embedder.ts +13 -4
  39. package/src/core/sqlite-event-store.ts +16 -0
  40. package/src/core/turn-state.ts +48 -0
  41. package/src/core/types.ts +1 -0
  42. package/src/hooks/post-tool-use.ts +47 -2
  43. package/src/hooks/semantic-daemon-client.ts +208 -0
  44. package/src/hooks/semantic-daemon.ts +276 -0
  45. package/src/hooks/session-start.ts +7 -0
  46. package/src/hooks/stop.ts +19 -4
  47. package/src/hooks/user-prompt-submit.ts +48 -40
  48. package/src/services/memory-service.ts +59 -16
  49. package/src/services/session-history-importer.ts +18 -0
  50. package/src/ui/app.js +48 -1
  51. 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
- this.retriever.setGraduationPipeline(this.graduation);
6077
- this.graduationWorker = createGraduationWorker(
6078
- this.sqliteStore,
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.analyticsStore,
6086
- { intervalMs: 3e4, batchSize: 500 }
6104
+ this.graduation
6087
6105
  );
6088
- this.syncWorker.start();
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.23");
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`);