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
@@ -1850,6 +1850,21 @@ var SQLiteEventStore = class {
1850
1850
  [id, eventId, sessionId, score, query.slice(0, 100)]
1851
1851
  );
1852
1852
  }
1853
+ /**
1854
+ * Get session IDs that have unevaluated retrievals (measured_at IS NULL).
1855
+ * Excludes the current session. Used to backfill sessions that ended without Stop hook.
1856
+ */
1857
+ async getUnevaluatedSessions(currentSessionId, limit = 5) {
1858
+ await this.initialize();
1859
+ const rows = sqliteAll(
1860
+ this.db,
1861
+ `SELECT DISTINCT session_id FROM memory_helpfulness
1862
+ WHERE measured_at IS NULL AND session_id != ?
1863
+ ORDER BY created_at DESC LIMIT ?`,
1864
+ [currentSessionId, limit]
1865
+ );
1866
+ return rows.map((r) => r.session_id);
1867
+ }
1853
1868
  /**
1854
1869
  * Evaluate helpfulness for all retrievals in a session
1855
1870
  * Called at session end - uses behavioral signals to compute score
@@ -2637,7 +2652,7 @@ var VectorStore = class {
2637
2652
 
2638
2653
  // src/core/embedder.ts
2639
2654
  import { pipeline } from "@huggingface/transformers";
2640
- var Embedder = class {
2655
+ var Embedder = class _Embedder {
2641
2656
  pipeline = null;
2642
2657
  modelName;
2643
2658
  activeModelName;
@@ -2668,6 +2683,11 @@ var Embedder = class {
2668
2683
  this.initialized = true;
2669
2684
  }
2670
2685
  }
2686
+ // ~4 chars per token; 512 tokens * 4 = 2048, use 2000 to be safe
2687
+ static MAX_CHARS = 2e3;
2688
+ truncate(text) {
2689
+ return text.length > _Embedder.MAX_CHARS ? text.slice(0, _Embedder.MAX_CHARS) : text;
2690
+ }
2671
2691
  /**
2672
2692
  * Generate embedding for a single text
2673
2693
  */
@@ -2676,10 +2696,11 @@ var Embedder = class {
2676
2696
  if (!this.pipeline) {
2677
2697
  throw new Error("Embedding pipeline not initialized");
2678
2698
  }
2679
- const output = await this.pipeline(text, {
2699
+ const output = await this.pipeline(this.truncate(text), {
2680
2700
  pooling: "mean",
2681
2701
  normalize: true,
2682
- truncation: true
2702
+ truncation: true,
2703
+ max_length: 512
2683
2704
  });
2684
2705
  const vector = Array.from(output.data);
2685
2706
  return {
@@ -2701,10 +2722,11 @@ var Embedder = class {
2701
2722
  for (let i = 0; i < texts.length; i += batchSize) {
2702
2723
  const batch = texts.slice(i, i + batchSize);
2703
2724
  for (const text of batch) {
2704
- const output = await this.pipeline(text, {
2725
+ const output = await this.pipeline(this.truncate(text), {
2705
2726
  pooling: "mean",
2706
2727
  normalize: true,
2707
- truncation: true
2728
+ truncation: true,
2729
+ max_length: 512
2708
2730
  });
2709
2731
  const vector = Array.from(output.data);
2710
2732
  results.push({
@@ -5970,6 +5992,7 @@ var MemoryService = class {
5970
5992
  projectPath = null;
5971
5993
  readOnly;
5972
5994
  lightweightMode;
5995
+ embeddingOnly;
5973
5996
  mdMirror;
5974
5997
  storagePath;
5975
5998
  constructor(config) {
@@ -5977,6 +6000,7 @@ var MemoryService = class {
5977
6000
  this.storagePath = storagePath;
5978
6001
  this.readOnly = config.readOnly ?? false;
5979
6002
  this.lightweightMode = config.lightweightMode ?? false;
6003
+ this.embeddingOnly = config.embeddingOnly ?? false;
5980
6004
  this.mdMirror = new MarkdownMirror2(process.cwd());
5981
6005
  if (!this.readOnly && !fs4.existsSync(storagePath)) {
5982
6006
  fs4.mkdirSync(storagePath, { recursive: true });
@@ -6050,19 +6074,21 @@ var MemoryService = class {
6050
6074
  this.embedder
6051
6075
  );
6052
6076
  this.vectorWorker.start();
6053
- this.retriever.setGraduationPipeline(this.graduation);
6054
- this.graduationWorker = createGraduationWorker(
6055
- this.sqliteStore,
6056
- this.graduation
6057
- );
6058
- this.graduationWorker.start();
6059
- if (this.analyticsStore) {
6060
- this.syncWorker = new SyncWorker(
6077
+ if (!this.embeddingOnly) {
6078
+ this.retriever.setGraduationPipeline(this.graduation);
6079
+ this.graduationWorker = createGraduationWorker(
6061
6080
  this.sqliteStore,
6062
- this.analyticsStore,
6063
- { intervalMs: 3e4, batchSize: 500 }
6081
+ this.graduation
6064
6082
  );
6065
- this.syncWorker.start();
6083
+ this.graduationWorker.start();
6084
+ if (this.analyticsStore) {
6085
+ this.syncWorker = new SyncWorker(
6086
+ this.sqliteStore,
6087
+ this.analyticsStore,
6088
+ { intervalMs: 3e4, batchSize: 500 }
6089
+ );
6090
+ this.syncWorker.start();
6091
+ }
6066
6092
  }
6067
6093
  const savedMode = await this.sqliteStore.getEndlessConfig("mode");
6068
6094
  if (savedMode === "endless") {
@@ -6786,6 +6812,19 @@ var MemoryService = class {
6786
6812
  await this.initialize();
6787
6813
  await this.sqliteStore.recordRetrieval(eventId, sessionId, score, query);
6788
6814
  }
6815
+ /**
6816
+ * Record a query-level retrieval trace (used by user-prompt-submit hook).
6817
+ * Feeds the retrieval_traces table that powers dashboard stats.
6818
+ */
6819
+ async recordQueryTrace(input) {
6820
+ await this.initialize();
6821
+ await this.sqliteStore.recordRetrievalTrace({
6822
+ ...input,
6823
+ candidateDetails: [],
6824
+ selectedDetails: [],
6825
+ fallbackTrace: []
6826
+ });
6827
+ }
6789
6828
  /**
6790
6829
  * Evaluate helpfulness of retrievals in a session (called at session end)
6791
6830
  */
@@ -6793,6 +6832,20 @@ var MemoryService = class {
6793
6832
  await this.initialize();
6794
6833
  await this.sqliteStore.evaluateSessionHelpfulness(sessionId);
6795
6834
  }
6835
+ /**
6836
+ * Backfill helpfulness evaluation for sessions that ended without Stop hook.
6837
+ * Call on first turn of a new session to catch missed evaluations.
6838
+ */
6839
+ async evaluatePendingSessions(currentSessionId) {
6840
+ await this.initialize();
6841
+ const sessions = await this.sqliteStore.getUnevaluatedSessions(currentSessionId, 5);
6842
+ for (const sid of sessions) {
6843
+ try {
6844
+ await this.sqliteStore.evaluateSessionHelpfulness(sid);
6845
+ } catch {
6846
+ }
6847
+ }
6848
+ }
6796
6849
  /**
6797
6850
  * Get most helpful memories ranked by helpfulness score
6798
6851
  */