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
@@ -1859,6 +1859,21 @@ var SQLiteEventStore = class {
1859
1859
  [id, eventId, sessionId, score, query.slice(0, 100)]
1860
1860
  );
1861
1861
  }
1862
+ /**
1863
+ * Get session IDs that have unevaluated retrievals (measured_at IS NULL).
1864
+ * Excludes the current session. Used to backfill sessions that ended without Stop hook.
1865
+ */
1866
+ async getUnevaluatedSessions(currentSessionId, limit = 5) {
1867
+ await this.initialize();
1868
+ const rows = sqliteAll(
1869
+ this.db,
1870
+ `SELECT DISTINCT session_id FROM memory_helpfulness
1871
+ WHERE measured_at IS NULL AND session_id != ?
1872
+ ORDER BY created_at DESC LIMIT ?`,
1873
+ [currentSessionId, limit]
1874
+ );
1875
+ return rows.map((r) => r.session_id);
1876
+ }
1862
1877
  /**
1863
1878
  * Evaluate helpfulness for all retrievals in a session
1864
1879
  * Called at session end - uses behavioral signals to compute score
@@ -2646,7 +2661,7 @@ var VectorStore = class {
2646
2661
 
2647
2662
  // src/core/embedder.ts
2648
2663
  import { pipeline } from "@huggingface/transformers";
2649
- var Embedder = class {
2664
+ var Embedder = class _Embedder {
2650
2665
  pipeline = null;
2651
2666
  modelName;
2652
2667
  activeModelName;
@@ -2677,6 +2692,11 @@ var Embedder = class {
2677
2692
  this.initialized = true;
2678
2693
  }
2679
2694
  }
2695
+ // ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
2696
+ static MAX_CHARS = 2e3;
2697
+ truncate(text) {
2698
+ return text.length > _Embedder.MAX_CHARS ? text.slice(0, _Embedder.MAX_CHARS) : text;
2699
+ }
2680
2700
  /**
2681
2701
  * Generate embedding for a single text
2682
2702
  */
@@ -2685,10 +2705,11 @@ var Embedder = class {
2685
2705
  if (!this.pipeline) {
2686
2706
  throw new Error("Embedding pipeline not initialized");
2687
2707
  }
2688
- const output = await this.pipeline(text, {
2708
+ const output = await this.pipeline(this.truncate(text), {
2689
2709
  pooling: "mean",
2690
2710
  normalize: true,
2691
- truncation: true
2711
+ truncation: true,
2712
+ max_length: 512
2692
2713
  });
2693
2714
  const vector = Array.from(output.data);
2694
2715
  return {
@@ -2710,10 +2731,11 @@ var Embedder = class {
2710
2731
  for (let i = 0; i < texts.length; i += batchSize) {
2711
2732
  const batch = texts.slice(i, i + batchSize);
2712
2733
  for (const text of batch) {
2713
- const output = await this.pipeline(text, {
2734
+ const output = await this.pipeline(this.truncate(text), {
2714
2735
  pooling: "mean",
2715
2736
  normalize: true,
2716
- truncation: true
2737
+ truncation: true,
2738
+ max_length: 512
2717
2739
  });
2718
2740
  const vector = Array.from(output.data);
2719
2741
  results.push({
@@ -5979,6 +6001,7 @@ var MemoryService = class {
5979
6001
  projectPath = null;
5980
6002
  readOnly;
5981
6003
  lightweightMode;
6004
+ embeddingOnly;
5982
6005
  mdMirror;
5983
6006
  storagePath;
5984
6007
  constructor(config) {
@@ -5986,6 +6009,7 @@ var MemoryService = class {
5986
6009
  this.storagePath = storagePath;
5987
6010
  this.readOnly = config.readOnly ?? false;
5988
6011
  this.lightweightMode = config.lightweightMode ?? false;
6012
+ this.embeddingOnly = config.embeddingOnly ?? false;
5989
6013
  this.mdMirror = new MarkdownMirror2(process.cwd());
5990
6014
  if (!this.readOnly && !fs4.existsSync(storagePath)) {
5991
6015
  fs4.mkdirSync(storagePath, { recursive: true });
@@ -6059,19 +6083,21 @@ var MemoryService = class {
6059
6083
  this.embedder
6060
6084
  );
6061
6085
  this.vectorWorker.start();
6062
- this.retriever.setGraduationPipeline(this.graduation);
6063
- this.graduationWorker = createGraduationWorker(
6064
- this.sqliteStore,
6065
- this.graduation
6066
- );
6067
- this.graduationWorker.start();
6068
- if (this.analyticsStore) {
6069
- this.syncWorker = new SyncWorker(
6086
+ if (!this.embeddingOnly) {
6087
+ this.retriever.setGraduationPipeline(this.graduation);
6088
+ this.graduationWorker = createGraduationWorker(
6070
6089
  this.sqliteStore,
6071
- this.analyticsStore,
6072
- { intervalMs: 3e4, batchSize: 500 }
6090
+ this.graduation
6073
6091
  );
6074
- this.syncWorker.start();
6092
+ this.graduationWorker.start();
6093
+ if (this.analyticsStore) {
6094
+ this.syncWorker = new SyncWorker(
6095
+ this.sqliteStore,
6096
+ this.analyticsStore,
6097
+ { intervalMs: 3e4, batchSize: 500 }
6098
+ );
6099
+ this.syncWorker.start();
6100
+ }
6075
6101
  }
6076
6102
  const savedMode = await this.sqliteStore.getEndlessConfig("mode");
6077
6103
  if (savedMode === "endless") {
@@ -6795,6 +6821,19 @@ var MemoryService = class {
6795
6821
  await this.initialize();
6796
6822
  await this.sqliteStore.recordRetrieval(eventId, sessionId, score, query);
6797
6823
  }
6824
+ /**
6825
+ * Record a query-level retrieval trace (used by user-prompt-submit hook).
6826
+ * Feeds the retrieval_traces table that powers dashboard stats.
6827
+ */
6828
+ async recordQueryTrace(input) {
6829
+ await this.initialize();
6830
+ await this.sqliteStore.recordRetrievalTrace({
6831
+ ...input,
6832
+ candidateDetails: [],
6833
+ selectedDetails: [],
6834
+ fallbackTrace: []
6835
+ });
6836
+ }
6798
6837
  /**
6799
6838
  * Evaluate helpfulness of retrievals in a session (called at session end)
6800
6839
  */
@@ -6802,6 +6841,20 @@ var MemoryService = class {
6802
6841
  await this.initialize();
6803
6842
  await this.sqliteStore.evaluateSessionHelpfulness(sessionId);
6804
6843
  }
6844
+ /**
6845
+ * Backfill helpfulness evaluation for sessions that ended without Stop hook.
6846
+ * Call on first turn of a new session to catch missed evaluations.
6847
+ */
6848
+ async evaluatePendingSessions(currentSessionId) {
6849
+ await this.initialize();
6850
+ const sessions = await this.sqliteStore.getUnevaluatedSessions(currentSessionId, 5);
6851
+ for (const sid of sessions) {
6852
+ try {
6853
+ await this.sqliteStore.evaluateSessionHelpfulness(sid);
6854
+ } catch {
6855
+ }
6856
+ }
6857
+ }
6805
6858
  /**
6806
6859
  * Get most helpful memories ranked by helpfulness score
6807
6860
  */