recallnest 1.4.1

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 (261) hide show
  1. package/.claude-plugin/CLAUDE.md +29 -0
  2. package/.claude-plugin/marketplace.json +18 -0
  3. package/.env.example +3 -0
  4. package/.github/workflows/ci.yml +28 -0
  5. package/.mcp.json +9 -0
  6. package/CHANGELOG.md +51 -0
  7. package/CLAUDE.md +42 -0
  8. package/DEMO.md +94 -0
  9. package/EVAL.md +89 -0
  10. package/FAILURES.md +75 -0
  11. package/LICENSE +21 -0
  12. package/OPERATIONS.md +59 -0
  13. package/README.md +427 -0
  14. package/README_CN.md +425 -0
  15. package/ROADMAP.md +198 -0
  16. package/assets/recallnest-workbench.png +0 -0
  17. package/assets/ui/app.js +807 -0
  18. package/assets/ui/index.html +154 -0
  19. package/assets/ui/styles.css +605 -0
  20. package/assets/wechat-qr.jpg +0 -0
  21. package/config.json.example +38 -0
  22. package/daily-recall.sh +87 -0
  23. package/daily-reflections/2026-03-05.md +15 -0
  24. package/daily-reflections/2026-03-06.md +15 -0
  25. package/daily-reflections/2026-03-07.md +7 -0
  26. package/daily-reflections/2026-03-08.md +34 -0
  27. package/daily-reflections/2026-03-09.md +35 -0
  28. package/daily-reflections/2026-03-10.md +36 -0
  29. package/daily-reflections/2026-03-11.md +35 -0
  30. package/daily-reflections/2026-03-12.md +36 -0
  31. package/daily-reflections/2026-03-13.md +35 -0
  32. package/daily-reflections/2026-03-14.md +35 -0
  33. package/daily-reflections/2026-03-15.md +35 -0
  34. package/daily-reflections/2026-03-16.md +41 -0
  35. package/daily-reflections/2026-03-17.md +10 -0
  36. package/daily-reflections/2026-03-18.md +10 -0
  37. package/daily-reflections/2026-03-19.md +10 -0
  38. package/daily-reflections/2026-03-20.md +10 -0
  39. package/daily-reflections/2026-03-21.md +10 -0
  40. package/daily-reflections/2026-03-22.md +10 -0
  41. package/daily-reflections/2026-03-23.md +10 -0
  42. package/daily-reflections/2026-03-24.md +10 -0
  43. package/daily-reflections/2026-03-25.md +32 -0
  44. package/daily-reflections/2026-03-26.md +37 -0
  45. package/daily-reflections/2026-03-27.md +35 -0
  46. package/daily-reflections/2026-03-28.md +35 -0
  47. package/daily-reflections/2026-03-29.md +35 -0
  48. package/daily-reflections/2026-03-30.md +36 -0
  49. package/daily-reflections/2026-03-31.md +35 -0
  50. package/daily-reflections/2026-04-01.md +35 -0
  51. package/docs/api-reference.md +1291 -0
  52. package/docs/architecture.md +168 -0
  53. package/docs/memory-boundary-contract.md +226 -0
  54. package/docs/memory-categories.md +119 -0
  55. package/docs/pipeline-audit-ultramemory.md +177 -0
  56. package/docs/plans/2026-03-08-query-expansion.md +270 -0
  57. package/docs/plans/2026-03-16-continuity-layer.md +196 -0
  58. package/docs/plans/mcp-tiering-plan.md +126 -0
  59. package/docs/research/recall-quality-audit-2026-03-22.md +153 -0
  60. package/docs/self-evolution.md +198 -0
  61. package/docs/superpowers/plans/2026-03-19-ingest-pipeline-upgrade.md +782 -0
  62. package/docs/superpowers/specs/2026-03-19-ingest-pipeline-upgrade-design.md +77 -0
  63. package/docs/superpowers/specs/2026-03-23-pull-claude-desktop-design.md +95 -0
  64. package/eval/cases.json +85 -0
  65. package/eval/continuity/case-seeds.json +90 -0
  66. package/eval/continuity/cases.json +302 -0
  67. package/eval/continuity/memory-seeds.json +26 -0
  68. package/eval/continuity/pattern-seeds.json +100 -0
  69. package/eval/continuity/results-2026-03-16.md +117 -0
  70. package/eval/continuity/results-2026-03-19.md +453 -0
  71. package/eval/continuity/runbook.md +110 -0
  72. package/eval/continuity/seed-cases.md +54 -0
  73. package/eval/reports/2026-03-06-abstract-query-baseline.md +96 -0
  74. package/eval/reports/2026-03-06-aws-ssh-baseline.md +82 -0
  75. package/eval/reports/2026-03-06-baseline.md +82 -0
  76. package/eval/reports/query-expansion.md +138 -0
  77. package/integrations/README.md +57 -0
  78. package/integrations/claude-code/README.md +133 -0
  79. package/integrations/claude-code/claude-md-snippet.md +31 -0
  80. package/integrations/claude-code/setup.sh +66 -0
  81. package/integrations/claude-code/smoke-continuity.sh +452 -0
  82. package/integrations/codex/README.md +49 -0
  83. package/integrations/codex/agents-md-snippet.md +31 -0
  84. package/integrations/codex/setup.sh +58 -0
  85. package/integrations/examples/claude-agent-sdk/memory-agent.ts +182 -0
  86. package/integrations/examples/langchain/memory-chain.py +111 -0
  87. package/integrations/examples/openai-agents-sdk/memory-agent.py +109 -0
  88. package/integrations/gemini-cli/README.md +57 -0
  89. package/integrations/gemini-cli/gemini-md-snippet.md +31 -0
  90. package/integrations/gemini-cli/setup.sh +64 -0
  91. package/integrations/shared/install-managed-block.sh +51 -0
  92. package/lm +19 -0
  93. package/package.json +50 -0
  94. package/plan-v1.2-distributable.md +105 -0
  95. package/scripts/archive-distilled-junk.ts +116 -0
  96. package/scripts/backfill-l0l1l2.ts +98 -0
  97. package/scripts/blind-scan.ts +588 -0
  98. package/scripts/cleanup-step1-exact-dups.ts +117 -0
  99. package/scripts/cleanup-step2-template-dups.ts +113 -0
  100. package/scripts/cleanup-step3-stale-content.ts +149 -0
  101. package/scripts/cleanup-step4-orphan-scopes.ts +131 -0
  102. package/scripts/delete-distilled-junk.ts +88 -0
  103. package/scripts/distill-facts.ts +305 -0
  104. package/scripts/health-check.ts +343 -0
  105. package/scripts/incremental-ingest.sh +71 -0
  106. package/scripts/mlp-health-check.ts +382 -0
  107. package/scripts/pull-claude-desktop.ts +515 -0
  108. package/scripts/re-ingest-cc.sh +66 -0
  109. package/scripts/rebalance-memory-health.ts +181 -0
  110. package/scripts/run-desktop-ingest.sh +11 -0
  111. package/scripts/start-server.sh +12 -0
  112. package/scripts/viz-dashboard.html +423 -0
  113. package/scripts/viz-export.ts +703 -0
  114. package/src/__tests__/access-tracker-novelty.test.ts +228 -0
  115. package/src/__tests__/auto-consolidation.test.ts +168 -0
  116. package/src/__tests__/auto-recall.test.ts +131 -0
  117. package/src/__tests__/batch-dedup.test.ts +65 -0
  118. package/src/__tests__/capture-engine.test.ts +1142 -0
  119. package/src/__tests__/capture-heuristic.test.ts +171 -0
  120. package/src/__tests__/cascade-forget.test.ts +210 -0
  121. package/src/__tests__/category-min-scores.test.ts +33 -0
  122. package/src/__tests__/confidence-tracker.test.ts +196 -0
  123. package/src/__tests__/conflict-advisor.test.ts +206 -0
  124. package/src/__tests__/conflict-escalation.test.ts +149 -0
  125. package/src/__tests__/conflict-lifecycle.test.ts +83 -0
  126. package/src/__tests__/conflict-output.test.ts +85 -0
  127. package/src/__tests__/conflict-store.test.ts +374 -0
  128. package/src/__tests__/consolidation-engine.test.ts +181 -0
  129. package/src/__tests__/context-composer-pins.test.ts +64 -0
  130. package/src/__tests__/context-composer-task-selection.test.ts +24 -0
  131. package/src/__tests__/context-renderer.test.ts +91 -0
  132. package/src/__tests__/continuity-eval.test.ts +275 -0
  133. package/src/__tests__/continuity-schema.test.ts +97 -0
  134. package/src/__tests__/core-recall-eval.test.ts +235 -0
  135. package/src/__tests__/core-summary.test.ts +109 -0
  136. package/src/__tests__/dedup-actions.test.ts +58 -0
  137. package/src/__tests__/deterministic-id.test.ts +39 -0
  138. package/src/__tests__/doctor.test.ts +303 -0
  139. package/src/__tests__/drill-down.test.ts +108 -0
  140. package/src/__tests__/embedder.test.ts +122 -0
  141. package/src/__tests__/entity-resolver.test.ts +58 -0
  142. package/src/__tests__/event-segmenter.test.ts +153 -0
  143. package/src/__tests__/hotness-blend.test.ts +76 -0
  144. package/src/__tests__/ingest.test.ts +360 -0
  145. package/src/__tests__/kg-extractor.test.ts +184 -0
  146. package/src/__tests__/kg-store.test.ts +198 -0
  147. package/src/__tests__/llm-consolidation.test.ts +226 -0
  148. package/src/__tests__/memory-boundaries.test.ts +237 -0
  149. package/src/__tests__/memory-health-rebalance.test.ts +144 -0
  150. package/src/__tests__/memory-output.test.ts +215 -0
  151. package/src/__tests__/multi-vector.test.ts +242 -0
  152. package/src/__tests__/ppr-traversal.test.ts +256 -0
  153. package/src/__tests__/preference-guard-smoke.test.ts +211 -0
  154. package/src/__tests__/preference-matcher.test.ts +230 -0
  155. package/src/__tests__/preference-slots.test.ts +106 -0
  156. package/src/__tests__/prospective-memory.test.ts +283 -0
  157. package/src/__tests__/query-entity-detector.test.ts +171 -0
  158. package/src/__tests__/query-expander.test.ts +34 -0
  159. package/src/__tests__/recall-mode.test.ts +64 -0
  160. package/src/__tests__/result-synthesizer.test.ts +198 -0
  161. package/src/__tests__/resume-context.test.ts +4743 -0
  162. package/src/__tests__/retrieval-trace.test.ts +213 -0
  163. package/src/__tests__/retriever-boundary-weight.test.ts +136 -0
  164. package/src/__tests__/retroactive-boost.test.ts +223 -0
  165. package/src/__tests__/scope-inventory.test.ts +200 -0
  166. package/src/__tests__/scope-policy.test.ts +69 -0
  167. package/src/__tests__/session-checkpoint.test.ts +259 -0
  168. package/src/__tests__/session-gc.test.ts +215 -0
  169. package/src/__tests__/store-scope-audit.test.ts +81 -0
  170. package/src/__tests__/temporal-parser.test.ts +241 -0
  171. package/src/__tests__/term-registry.test.ts +121 -0
  172. package/src/__tests__/tool-output-compressor.test.ts +133 -0
  173. package/src/__tests__/version-manager.test.ts +213 -0
  174. package/src/__tests__/workflow-observation.test.ts +400 -0
  175. package/src/access-tracker.ts +316 -0
  176. package/src/adaptive-retrieval.ts +64 -0
  177. package/src/api-server.ts +667 -0
  178. package/src/asset-sync.ts +79 -0
  179. package/src/auto-consolidation.ts +133 -0
  180. package/src/auto-gc.ts +124 -0
  181. package/src/auto-recall.ts +95 -0
  182. package/src/capture-engine.ts +931 -0
  183. package/src/capture-heuristic.ts +177 -0
  184. package/src/cascade-forget.ts +135 -0
  185. package/src/chunker.ts +253 -0
  186. package/src/cli.ts +1917 -0
  187. package/src/confidence-tracker.ts +166 -0
  188. package/src/conflict-advisor.ts +433 -0
  189. package/src/conflict-engine.ts +181 -0
  190. package/src/conflict-escalation.ts +138 -0
  191. package/src/conflict-lifecycle.ts +67 -0
  192. package/src/conflict-output.ts +218 -0
  193. package/src/conflict-schema.ts +130 -0
  194. package/src/conflict-store.ts +127 -0
  195. package/src/consolidation-engine.ts +256 -0
  196. package/src/context-composer-pins.ts +183 -0
  197. package/src/context-composer-queries.ts +121 -0
  198. package/src/context-composer-scope.ts +56 -0
  199. package/src/context-composer-stable-selection.ts +200 -0
  200. package/src/context-composer-stable.ts +217 -0
  201. package/src/context-composer-task-fallbacks.ts +36 -0
  202. package/src/context-composer-task-ranking.ts +197 -0
  203. package/src/context-composer-task-results.ts +137 -0
  204. package/src/context-composer-task-selection.ts +342 -0
  205. package/src/context-composer-text.ts +57 -0
  206. package/src/context-composer.ts +300 -0
  207. package/src/context-renderer.ts +179 -0
  208. package/src/decay-engine.ts +189 -0
  209. package/src/doctor.ts +543 -0
  210. package/src/embedder.ts +557 -0
  211. package/src/entity-resolver.ts +131 -0
  212. package/src/eval.ts +632 -0
  213. package/src/event-segmenter.ts +216 -0
  214. package/src/ingest.ts +1822 -0
  215. package/src/kg-extractor.ts +217 -0
  216. package/src/kg-store.ts +353 -0
  217. package/src/legacy-scope-review.ts +53 -0
  218. package/src/llm-client.ts +629 -0
  219. package/src/llm-consolidation.ts +168 -0
  220. package/src/mcp-server.ts +1337 -0
  221. package/src/memory-assets.ts +475 -0
  222. package/src/memory-boundaries.ts +354 -0
  223. package/src/memory-health-rebalance.ts +220 -0
  224. package/src/memory-output.ts +474 -0
  225. package/src/memory-schema.ts +184 -0
  226. package/src/multi-vector.ts +234 -0
  227. package/src/noise-filter.ts +155 -0
  228. package/src/ppr-traversal.ts +279 -0
  229. package/src/preference-matcher.ts +174 -0
  230. package/src/preference-slots.ts +259 -0
  231. package/src/prospective-memory.ts +198 -0
  232. package/src/query-entity-detector.ts +207 -0
  233. package/src/query-expander.ts +98 -0
  234. package/src/result-synthesizer.ts +114 -0
  235. package/src/retrieval-profiles.ts +101 -0
  236. package/src/retrieval-trace.ts +94 -0
  237. package/src/retriever.ts +1338 -0
  238. package/src/retroactive-boost.ts +154 -0
  239. package/src/rif.ts +73 -0
  240. package/src/runtime-config.ts +172 -0
  241. package/src/schema-utils.ts +56 -0
  242. package/src/scope-inventory.ts +310 -0
  243. package/src/scope-policy.ts +142 -0
  244. package/src/session-engine.ts +168 -0
  245. package/src/session-output.ts +96 -0
  246. package/src/session-schema.ts +60 -0
  247. package/src/session-store.ts +293 -0
  248. package/src/stderr-log.ts +16 -0
  249. package/src/store.ts +903 -0
  250. package/src/temporal-parser.ts +318 -0
  251. package/src/term-registry.ts +791 -0
  252. package/src/tool-output-compressor.ts +115 -0
  253. package/src/tracker.ts +57 -0
  254. package/src/ui-server.ts +430 -0
  255. package/src/version-manager.ts +172 -0
  256. package/src/workflow-observation-engine.ts +316 -0
  257. package/src/workflow-observation-managed.ts +83 -0
  258. package/src/workflow-observation-output.ts +86 -0
  259. package/src/workflow-observation-schema.ts +87 -0
  260. package/src/workflow-observation-scope-review.ts +409 -0
  261. package/src/workflow-observation-store.ts +69 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Tests for Tier 3.5: Result Synthesizer
3
+ *
4
+ * Validates:
5
+ * 1. synthesize() with enough fragments calls LLM
6
+ * 2. synthesize() returns fragments when below threshold
7
+ * 3. synthesize() returns fragments when no LLM
8
+ * 4. synthesize() handles LLM failure gracefully
9
+ * 5. synthesizeSection() respects RECALLNEST_SYNTHESIZE env
10
+ * 6. synthesizeSection() passes through when disabled
11
+ * 7. Single fragment returned as-is (no LLM call)
12
+ */
13
+ import { describe, expect, it, beforeEach, afterEach } from "bun:test";
14
+ import {
15
+ synthesize,
16
+ synthesizeSection,
17
+ isSynthesisEnabled,
18
+ DEFAULT_SYNTHESIZER_CONFIG,
19
+ } from "../result-synthesizer.js";
20
+ import type { LLMClient } from "../llm-client.js";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Mock LLM Client
24
+ // ---------------------------------------------------------------------------
25
+
26
+ function createMockLLM(response: string | null = "Synthesized narrative."): LLMClient & { calls: Array<{ fragments: string[]; query: string }> } {
27
+ const calls: Array<{ fragments: string[]; query: string }> = [];
28
+ return {
29
+ calls,
30
+ async synthesizeFragments(fragments: string[], query: string) {
31
+ calls.push({ fragments, query });
32
+ return response;
33
+ },
34
+ } as any;
35
+ }
36
+
37
+ function createFailingLLM(): LLMClient {
38
+ return {
39
+ async synthesizeFragments() {
40
+ throw new Error("LLM connection failed");
41
+ },
42
+ } as any;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // synthesize() tests
47
+ // ---------------------------------------------------------------------------
48
+
49
+ describe("synthesize", () => {
50
+ it("calls LLM when fragments >= minFragments", async () => {
51
+ const llm = createMockLLM("Combined narrative about user preferences.");
52
+ const fragments = ["Pref A: dark mode", "Pref B: vim keys", "Pref C: compact layout"];
53
+
54
+ const result = await synthesize(fragments, "user preferences", llm);
55
+
56
+ expect(result.synthesized).toBe(true);
57
+ expect(result.text).toBe("Combined narrative about user preferences.");
58
+ expect(result.reason).toBe("ok");
59
+ expect(llm.calls.length).toBe(1);
60
+ expect(llm.calls[0].query).toBe("user preferences");
61
+ });
62
+
63
+ it("skips synthesis when fragments < minFragments", async () => {
64
+ const llm = createMockLLM();
65
+ const fragments = ["Only one", "Two items"];
66
+
67
+ const result = await synthesize(fragments, "test", llm);
68
+
69
+ expect(result.synthesized).toBe(false);
70
+ expect(result.reason).toBe("below-threshold");
71
+ expect(result.fragments).toEqual(fragments);
72
+ expect(llm.calls.length).toBe(0);
73
+ });
74
+
75
+ it("skips synthesis when no LLM provided", async () => {
76
+ const fragments = ["A", "B", "C"];
77
+ const result = await synthesize(fragments, "test", null);
78
+
79
+ expect(result.synthesized).toBe(false);
80
+ expect(result.reason).toBe("no-llm");
81
+ });
82
+
83
+ it("handles LLM failure gracefully", async () => {
84
+ const llm = createFailingLLM();
85
+ const fragments = ["A", "B", "C"];
86
+
87
+ const result = await synthesize(fragments, "test", llm);
88
+
89
+ expect(result.synthesized).toBe(false);
90
+ expect(result.reason).toBe("error");
91
+ expect(result.fragments).toEqual(fragments);
92
+ });
93
+
94
+ it("handles empty LLM response", async () => {
95
+ const llm = createMockLLM(null);
96
+ const fragments = ["A", "B", "C"];
97
+
98
+ const result = await synthesize(fragments, "test", llm);
99
+
100
+ expect(result.synthesized).toBe(false);
101
+ expect(result.reason).toBe("empty-response");
102
+ });
103
+
104
+ it("handles empty fragments array", async () => {
105
+ const llm = createMockLLM();
106
+ const result = await synthesize([], "test", llm);
107
+
108
+ expect(result.synthesized).toBe(false);
109
+ expect(result.reason).toBe("below-threshold");
110
+ });
111
+
112
+ it("respects custom minFragments config", async () => {
113
+ const llm = createMockLLM("ok");
114
+ const fragments = ["A", "B"];
115
+
116
+ // Default minFragments=3, so 2 items would be below threshold
117
+ const result2 = await synthesize(fragments, "test", llm, { ...DEFAULT_SYNTHESIZER_CONFIG, minFragments: 2 });
118
+ expect(result2.synthesized).toBe(true);
119
+ });
120
+ });
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // synthesizeSection() tests
124
+ // ---------------------------------------------------------------------------
125
+
126
+ describe("synthesizeSection", () => {
127
+ const originalEnv = process.env.RECALLNEST_SYNTHESIZE;
128
+
129
+ afterEach(() => {
130
+ if (originalEnv !== undefined) {
131
+ process.env.RECALLNEST_SYNTHESIZE = originalEnv;
132
+ } else {
133
+ delete process.env.RECALLNEST_SYNTHESIZE;
134
+ }
135
+ });
136
+
137
+ it("returns original items when RECALLNEST_SYNTHESIZE is not set", async () => {
138
+ delete process.env.RECALLNEST_SYNTHESIZE;
139
+ const items = ["A", "B", "C", "D"];
140
+ const llm = createMockLLM("synthesized");
141
+
142
+ const result = await synthesizeSection(items, "test", llm);
143
+
144
+ expect(result).toEqual(items);
145
+ expect(llm.calls.length).toBe(0);
146
+ });
147
+
148
+ it("synthesizes when RECALLNEST_SYNTHESIZE=true", async () => {
149
+ process.env.RECALLNEST_SYNTHESIZE = "true";
150
+ const items = ["Profile: developer", "Preference: dark mode", "Entity: project X"];
151
+ const llm = createMockLLM("User is a developer who prefers dark mode and works on project X.");
152
+
153
+ const result = await synthesizeSection(items, "user context", llm);
154
+
155
+ expect(result).toEqual(["User is a developer who prefers dark mode and works on project X."]);
156
+ });
157
+
158
+ it("returns original items when synthesis fails", async () => {
159
+ process.env.RECALLNEST_SYNTHESIZE = "true";
160
+ const items = ["A", "B", "C"];
161
+ const llm = createFailingLLM();
162
+
163
+ const result = await synthesizeSection(items, "test", llm);
164
+
165
+ expect(result).toEqual(items);
166
+ });
167
+ });
168
+
169
+ // ---------------------------------------------------------------------------
170
+ // isSynthesisEnabled() tests
171
+ // ---------------------------------------------------------------------------
172
+
173
+ describe("isSynthesisEnabled", () => {
174
+ const originalEnv = process.env.RECALLNEST_SYNTHESIZE;
175
+
176
+ afterEach(() => {
177
+ if (originalEnv !== undefined) {
178
+ process.env.RECALLNEST_SYNTHESIZE = originalEnv;
179
+ } else {
180
+ delete process.env.RECALLNEST_SYNTHESIZE;
181
+ }
182
+ });
183
+
184
+ it("returns false when env not set", () => {
185
+ delete process.env.RECALLNEST_SYNTHESIZE;
186
+ expect(isSynthesisEnabled()).toBe(false);
187
+ });
188
+
189
+ it("returns true when env is 'true'", () => {
190
+ process.env.RECALLNEST_SYNTHESIZE = "true";
191
+ expect(isSynthesisEnabled()).toBe(true);
192
+ });
193
+
194
+ it("returns false when env is other value", () => {
195
+ process.env.RECALLNEST_SYNTHESIZE = "false";
196
+ expect(isSynthesisEnabled()).toBe(false);
197
+ });
198
+ });