recallnest 2.5.4 → 2.6.0

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 (81) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +34 -9
  3. package/README_CN.md +37 -12
  4. package/package.json +4 -4
  5. package/src/__tests__/access-tracker-novelty.test.ts +66 -0
  6. package/src/__tests__/api-server-security.test.ts +102 -0
  7. package/src/__tests__/belief-history.test.ts +220 -0
  8. package/src/__tests__/capture-engine.test.ts +93 -3
  9. package/src/__tests__/category-min-scores.test.ts +83 -0
  10. package/src/__tests__/cluster-consolidation.test.ts +167 -4
  11. package/src/__tests__/conflict-store.test.ts +46 -2
  12. package/src/__tests__/consolidation-engine.test.ts +97 -0
  13. package/src/__tests__/contracts.test.ts +46 -0
  14. package/src/__tests__/decay-exemption.test.ts +45 -0
  15. package/src/__tests__/dream-pipeline.test.ts +464 -5
  16. package/src/__tests__/freshness.test.ts +186 -0
  17. package/src/__tests__/hotness-blend.test.ts +207 -0
  18. package/src/__tests__/llm-client-pivot.test.ts +628 -0
  19. package/src/__tests__/maintenance-locks.test.ts +15 -0
  20. package/src/__tests__/memory-boundaries.test.ts +32 -0
  21. package/src/__tests__/noise-prototype-bank.test.ts +280 -0
  22. package/src/__tests__/pii-redact.test.ts +19 -0
  23. package/src/__tests__/pivot-apply.test.ts +373 -0
  24. package/src/__tests__/pivot-distill-supervisor.test.ts +457 -0
  25. package/src/__tests__/pivot-distill.test.ts +1178 -0
  26. package/src/__tests__/preference-matcher.test.ts +54 -2
  27. package/src/__tests__/pull-from-macbook-sync-only.test.ts +679 -0
  28. package/src/__tests__/recall-gate.test.ts +266 -0
  29. package/src/__tests__/resolve-db-path.test.ts +130 -0
  30. package/src/__tests__/resolve-tier.test.ts +64 -0
  31. package/src/__tests__/retrieval-eval.test.ts +163 -0
  32. package/src/__tests__/retriever-lazy-init-dispatch.test.ts +123 -0
  33. package/src/__tests__/skill-promotion.test.ts +53 -0
  34. package/src/__tests__/stable-context-length-contract.test.ts +62 -0
  35. package/src/__tests__/store-read-consistency.test.ts +122 -0
  36. package/src/__tests__/store-schema-migration.test.ts +87 -0
  37. package/src/__tests__/store-upsert.test.ts +17 -0
  38. package/src/__tests__/term-registry-terminal-roster.test.ts +127 -0
  39. package/src/__tests__/ui-server-security.test.ts +93 -0
  40. package/src/access-tracker.ts +53 -3
  41. package/src/adaptive-retrieval.ts +11 -0
  42. package/src/api-server.ts +9 -2
  43. package/src/auto-recall.ts +61 -1
  44. package/src/belief-history.ts +164 -0
  45. package/src/capture-engine.ts +88 -11
  46. package/src/cli.ts +109 -14
  47. package/src/conflict-engine.ts +24 -3
  48. package/src/consolidation-engine.ts +146 -12
  49. package/src/context-composer-stable-selection.ts +7 -1
  50. package/src/data-checkup.ts +1 -1
  51. package/src/decay-engine.ts +59 -7
  52. package/src/doctor.ts +4 -2
  53. package/src/dream-pipeline.ts +359 -11
  54. package/src/env-config.ts +41 -0
  55. package/src/eval.ts +154 -11
  56. package/src/freshness.ts +192 -0
  57. package/src/http-request-policy.ts +148 -0
  58. package/src/ingest.ts +277 -2
  59. package/src/kg-extractor.ts +7 -1
  60. package/src/llm-client.ts +106 -11
  61. package/src/mcp-server.ts +2 -1
  62. package/src/mcp-tools-advanced.ts +13 -0
  63. package/src/mcp-tools-core.ts +5 -1
  64. package/src/memory-boundaries.ts +23 -2
  65. package/src/memory-health-rebalance.ts +4 -0
  66. package/src/memory-output.ts +16 -2
  67. package/src/memory-store-port.ts +3 -0
  68. package/src/noise-prototype-bank.ts +408 -0
  69. package/src/pii-detector.ts +52 -2
  70. package/src/pivot-apply.ts +399 -0
  71. package/src/preference-matcher.ts +37 -3
  72. package/src/recall-gate.ts +305 -0
  73. package/src/retrieval-profiles.ts +46 -4
  74. package/src/retriever.ts +95 -18
  75. package/src/retroactive-boost.ts +6 -2
  76. package/src/runtime-config.ts +70 -5
  77. package/src/skill-promotion.ts +58 -2
  78. package/src/store.ts +54 -19
  79. package/src/term-registry.ts +35 -0
  80. package/src/ui-server.ts +7 -0
  81. package/src/version.ts +19 -0
@@ -18,6 +18,7 @@ import {
18
18
  } from "../preference-matcher.js";
19
19
  import type { MemoryEntry, MemorySearchResult } from "../store.js";
20
20
  import type { LLMClient, DedupDecision } from "../llm-client.js";
21
+ import { parseEvolution } from "../memory-evolution.js";
21
22
 
22
23
  // ---------------------------------------------------------------------------
23
24
  // Helpers
@@ -37,7 +38,8 @@ function makeEntry(id: string, text: string, category = "preferences"): MemoryEn
37
38
  }
38
39
 
39
40
  function createMockStore(searchResults: MemorySearchResult[] = []) {
40
- const updates: Array<{ id: string; text?: string }> = [];
41
+ const updates: Array<{ id: string; text?: string; metadata?: string }> = [];
42
+ const archived: MemoryEntry[] = [];
41
43
  const data = new Map<string, MemoryEntry>();
42
44
  for (const r of searchResults) {
43
45
  data.set(r.entry.id, { ...r.entry });
@@ -45,6 +47,7 @@ function createMockStore(searchResults: MemorySearchResult[] = []) {
45
47
 
46
48
  return {
47
49
  updates,
50
+ archived,
48
51
  data,
49
52
  async vectorSearch(
50
53
  _vector: number[],
@@ -54,18 +57,42 @@ function createMockStore(searchResults: MemorySearchResult[] = []) {
54
57
  ) {
55
58
  return searchResults;
56
59
  },
60
+ async getById(id: string) {
61
+ return data.get(id) ?? null;
62
+ },
63
+ // Belief-history rows land here rather than in `data` — merging must archive the
64
+ // version it replaces, so tests need to see what was written.
65
+ async upsert(entry: MemoryEntry) {
66
+ archived.push(entry);
67
+ return entry;
68
+ },
69
+ async store(entry: MemoryEntry) {
70
+ archived.push(entry);
71
+ return entry;
72
+ },
57
73
  async update(id: string, upd: { text?: string; metadata?: string }, _scope?: string[]) {
58
74
  const entry = data.get(id);
59
75
  if (!entry) return null;
60
76
  if (upd.text) {
61
77
  entry.text = upd.text;
62
- updates.push({ id, text: upd.text });
63
78
  }
79
+ if (upd.metadata) {
80
+ entry.metadata = upd.metadata;
81
+ }
82
+ updates.push({ id, text: upd.text, metadata: upd.metadata });
64
83
  return entry;
65
84
  },
66
85
  };
67
86
  }
68
87
 
88
+ const mockDeps = {
89
+ embedder: {
90
+ async embedPassage(text: string) {
91
+ return [text.length, 1, 0];
92
+ },
93
+ },
94
+ };
95
+
69
96
  function createMockLLM(dedupResult: DedupDecision, synthResult: string | null = "merged preference text"): LLMClient {
70
97
  return {
71
98
  async dedupDecision(_newText: string, _existingText: string) {
@@ -93,6 +120,22 @@ describe("matchPreference", () => {
93
120
  expect(result.reason).toBe("no-similar-preferences");
94
121
  });
95
122
 
123
+ it("ignores superseded belief-history rows as match candidates", async () => {
124
+ // store.vectorSearch() filters by scope only, so archived versions of a preference
125
+ // surface here alongside live ones. Matching against one would drop the incoming
126
+ // preference as a duplicate of a belief that is no longer held.
127
+ const archivedRow = makeEntry("p-old", "I prefer dark mode");
128
+ archivedRow.metadata = JSON.stringify({
129
+ evolution: { status: "superseded", validUntil: Date.now(), supersededBy: "p-live" },
130
+ });
131
+ const store = createMockStore([{ entry: archivedRow, score: 0.99 }]);
132
+
133
+ const result = await matchPreference("I prefer dark mode", vector, scope, store as any, null);
134
+
135
+ expect(result.action).toBe("create");
136
+ expect(result.reason).toBe("no-similar-preferences");
137
+ });
138
+
96
139
  it("returns create when matches are non-preference category", async () => {
97
140
  const store = createMockStore([
98
141
  { entry: makeEntry("e1", "some entity", "entities"), score: 0.85 },
@@ -190,6 +233,7 @@ describe("applyPreferenceMatch", () => {
190
233
  { action: "skip", reason: "duplicate" },
191
234
  store as any,
192
235
  "project:test",
236
+ mockDeps,
193
237
  );
194
238
 
195
239
  expect(result.handled).toBe(true);
@@ -209,12 +253,19 @@ describe("applyPreferenceMatch", () => {
209
253
  },
210
254
  store as any,
211
255
  "project:test",
256
+ mockDeps,
212
257
  );
213
258
 
214
259
  expect(result.handled).toBe(true);
215
260
  expect(result.entry).toBeDefined();
216
261
  expect(store.updates.length).toBe(1);
217
262
  expect(store.updates[0].text).toBe("merged preference text");
263
+
264
+ // Merging replaces the target's wording — the pre-merge version must survive.
265
+ expect(store.archived).toHaveLength(1);
266
+ expect(store.archived[0].text).toBe("old preference text");
267
+ expect(parseEvolution(store.archived[0].metadata).status).toBe("superseded");
268
+ expect(parseEvolution(store.updates[0].metadata).supersedes).toBe(store.archived[0].id);
218
269
  });
219
270
 
220
271
  it("returns not handled for create action", async () => {
@@ -223,6 +274,7 @@ describe("applyPreferenceMatch", () => {
223
274
  { action: "create", reason: "new" },
224
275
  store as any,
225
276
  "project:test",
277
+ mockDeps,
226
278
  );
227
279
 
228
280
  expect(result.handled).toBe(false);