recallnest 1.4.1 → 2.2.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 (166) hide show
  1. package/.dockerignore +11 -0
  2. package/CLAUDE.md +7 -3
  3. package/Dockerfile +20 -0
  4. package/README.md +202 -130
  5. package/README_CN.md +263 -190
  6. package/ROADMAP.md +128 -81
  7. package/SKILL.md +250 -0
  8. package/assets/dashboard.png +0 -0
  9. package/assets/knowledge-graph.png +0 -0
  10. package/assets/screenshots/ui-full.png +0 -0
  11. package/assets/screenshots/ui-overview.png +0 -0
  12. package/assets/screenshots/ui-search.png +0 -0
  13. package/assets/ui/app.js +227 -3
  14. package/assets/ui/index.html +81 -11
  15. package/assets/ui/styles.css +178 -2
  16. package/bin/recallnest.mjs +62 -0
  17. package/bun.lock +337 -0
  18. package/daily-recall.sh +23 -0
  19. package/daily-reflections/2026-04-02.md +35 -0
  20. package/daily-reflections/2026-04-03.md +37 -0
  21. package/daily-reflections/2026-04-04.md +37 -0
  22. package/daily-reflections/2026-04-05.md +35 -0
  23. package/daily-reflections/2026-04-06.md +36 -0
  24. package/daily-reflections/2026-04-07.md +35 -0
  25. package/daily-reflections/2026-04-08.md +35 -0
  26. package/daily-reflections/2026-04-12.md +35 -0
  27. package/data/alias-map.example.json +22 -0
  28. package/docs/plans/2026-04-01-recall-boost.md +100 -0
  29. package/docs/superpowers/plans/2026-04-11-constructive-retrieval-emotion.md +1195 -0
  30. package/docs/superpowers/specs/2026-04-11-constructive-retrieval-emotion-design.md +398 -0
  31. package/glama.json +6 -0
  32. package/integrations/setup-all.sh +31 -0
  33. package/package.json +10 -3
  34. package/plan-philosophy-upgrades.md +363 -0
  35. package/plan-product-upgrades.md +155 -0
  36. package/plan.md +140 -0
  37. package/research-memory-evolution.md +440 -0
  38. package/research-rl-agent-memory.md +330 -0
  39. package/scripts/backfill-anchors.ts +130 -0
  40. package/scripts/diagnose-vector-search.ts +132 -0
  41. package/scripts/migrate-language.ts +75 -0
  42. package/scripts/verify-emotion-decay.ts +148 -0
  43. package/src/__tests__/activity-counter.test.ts +96 -0
  44. package/src/__tests__/admission-control.test.ts +206 -0
  45. package/src/__tests__/anchor-generator.test.ts +67 -0
  46. package/src/__tests__/babel-integration.test.ts +139 -0
  47. package/src/__tests__/capture-engine.test.ts +5 -3
  48. package/src/__tests__/cascade-forget.test.ts +1 -1
  49. package/src/__tests__/cc8-cc1-essential-context.test.ts +298 -0
  50. package/src/__tests__/circuit-breaker.test.ts +99 -0
  51. package/src/__tests__/cluster-consolidation.test.ts +712 -0
  52. package/src/__tests__/confidence-metadata.test.ts +125 -0
  53. package/src/__tests__/confidence-tracker.test.ts +53 -0
  54. package/src/__tests__/consolidation-engine.test.ts +1 -1
  55. package/src/__tests__/constructive-retrieval.test.ts +509 -0
  56. package/src/__tests__/context-collapse-renderer.test.ts +214 -0
  57. package/src/__tests__/context-reconstructor.test.ts +65 -0
  58. package/src/__tests__/conversation-importer.test.ts +692 -0
  59. package/src/__tests__/data-checkup.test.ts +225 -0
  60. package/src/__tests__/decay-exemption.test.ts +84 -0
  61. package/src/__tests__/dedup-guard.test.ts +39 -0
  62. package/src/__tests__/distill-lock.test.ts +176 -0
  63. package/src/__tests__/dream-pipeline.test.ts +232 -0
  64. package/src/__tests__/emotion-decay.test.ts +46 -0
  65. package/src/__tests__/emotion-detector.test.ts +56 -0
  66. package/src/__tests__/emotion-phase1-integration.test.ts +153 -0
  67. package/src/__tests__/emotion-retrieval.test.ts +56 -0
  68. package/src/__tests__/ethics-layer.test.ts +476 -0
  69. package/src/__tests__/feature-flags-integration.test.ts +54 -0
  70. package/src/__tests__/frequency-tracker.test.ts +113 -0
  71. package/src/__tests__/graph-export.test.ts +489 -0
  72. package/src/__tests__/interference-detector.test.ts +203 -0
  73. package/src/__tests__/mcp-efficiency.test.ts +288 -0
  74. package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +243 -0
  75. package/src/__tests__/memory-evolution.test.ts +271 -0
  76. package/src/__tests__/memory-lint.test.ts +271 -0
  77. package/src/__tests__/narrative-architecture.test.ts +456 -0
  78. package/src/__tests__/pending-review.test.ts +77 -0
  79. package/src/__tests__/post-retrieval-filter.test.ts +178 -0
  80. package/src/__tests__/prediction-engine.test.ts +349 -0
  81. package/src/__tests__/predictive-prospective-memory.test.ts +416 -0
  82. package/src/__tests__/preference-slots.test.ts +99 -0
  83. package/src/__tests__/query-expander-alias.test.ts +89 -0
  84. package/src/__tests__/recall-governor.test.ts +227 -0
  85. package/src/__tests__/resume-context.test.ts +156 -1
  86. package/src/__tests__/salience-scoring.test.ts +106 -0
  87. package/src/__tests__/session-distiller.test.ts +611 -0
  88. package/src/__tests__/skill-engine.test.ts +546 -0
  89. package/src/__tests__/skill-promotion.test.ts +349 -0
  90. package/src/__tests__/supersede-chain.test.ts +154 -0
  91. package/src/__tests__/temporal-validity.test.ts +160 -0
  92. package/src/__tests__/topic-tag.test.ts +113 -0
  93. package/src/__tests__/trusted-memory.test.ts +828 -0
  94. package/src/__tests__/write-verifier.test.ts +122 -0
  95. package/src/access-tracker.ts +3 -2
  96. package/src/activity-counter.ts +109 -0
  97. package/src/adaptive-retrieval.ts +6 -5
  98. package/src/admission-control.ts +163 -0
  99. package/src/anchor-generator.ts +80 -0
  100. package/src/api-server.ts +10 -0
  101. package/src/audit-log.ts +122 -0
  102. package/src/auto-gc.ts +98 -29
  103. package/src/auto-recall.ts +33 -4
  104. package/src/capture-engine.ts +372 -23
  105. package/src/cascade-forget.ts +3 -2
  106. package/src/cli.ts +107 -5
  107. package/src/compat.ts +22 -0
  108. package/src/confidence-tracker.ts +210 -47
  109. package/src/conflict-store.ts +12 -3
  110. package/src/consolidation-engine.ts +296 -8
  111. package/src/context-collapse-renderer.ts +222 -0
  112. package/src/context-composer-stable-selection.ts +5 -1
  113. package/src/context-composer.ts +239 -4
  114. package/src/context-reconstructor.ts +404 -0
  115. package/src/conversation-importer.ts +441 -0
  116. package/src/data-checkup.ts +232 -0
  117. package/src/decay-engine.ts +84 -0
  118. package/src/distill-lock.ts +122 -0
  119. package/src/doctor.ts +8 -10
  120. package/src/dream-pipeline.ts +227 -0
  121. package/src/emotion-detector.ts +90 -0
  122. package/src/eval.ts +3 -2
  123. package/src/forget-engine.ts +269 -0
  124. package/src/frequency-tracker.ts +175 -0
  125. package/src/graph-export.ts +644 -0
  126. package/src/ingest.ts +36 -6
  127. package/src/interference-detector.ts +187 -0
  128. package/src/kg-extractor.ts +15 -24
  129. package/src/kg-store.ts +2 -1
  130. package/src/language-hook.ts +67 -0
  131. package/src/llm-client.ts +289 -6
  132. package/src/mcp-server.ts +707 -80
  133. package/src/memory-assets.ts +22 -5
  134. package/src/memory-evolution.ts +365 -0
  135. package/src/memory-lint.ts +429 -0
  136. package/src/memory-output.ts +69 -0
  137. package/src/memory-schema.ts +74 -2
  138. package/src/narrative-schema.ts +79 -0
  139. package/src/narrative-tagger.ts +164 -0
  140. package/src/pii-detector.ts +98 -0
  141. package/src/post-retrieval-filter.ts +76 -0
  142. package/src/prediction-engine.ts +253 -0
  143. package/src/preference-slots.ts +65 -2
  144. package/src/prospective-memory.ts +215 -0
  145. package/src/query-expander.ts +67 -0
  146. package/src/recall-governor.ts +183 -0
  147. package/src/retention-policy.ts +90 -0
  148. package/src/retriever.ts +828 -55
  149. package/src/rif.ts +34 -8
  150. package/src/runtime-config.ts +23 -8
  151. package/src/scope-inventory.ts +4 -3
  152. package/src/scope-policy.ts +4 -0
  153. package/src/session-distiller.ts +307 -0
  154. package/src/session-output.ts +40 -0
  155. package/src/session-schema.ts +37 -0
  156. package/src/session-store.ts +7 -2
  157. package/src/skill-engine.ts +208 -0
  158. package/src/skill-promotion.ts +293 -0
  159. package/src/skill-schema.ts +39 -0
  160. package/src/store.ts +96 -10
  161. package/src/topic-tag.ts +66 -0
  162. package/src/tracker.ts +3 -1
  163. package/src/ui-server.ts +80 -8
  164. package/src/workflow-observation-scope-review.ts +2 -1
  165. package/src/workflow-observation-store.ts +2 -1
  166. package/src/write-verifier.ts +126 -0
@@ -0,0 +1,363 @@
1
+ # Implementation Plan: Memory Philosophy-Driven RecallNest Upgrades
2
+
3
+ > Date: 2026-04-11
4
+ > Status: ✅ ALL 5 PHASES COMPLETE (commits a9e53d3..d150b8d, 1391 tests / 0 fail)
5
+ > Source: Memory philosophy research map + Codex code analysis + Explorer codebase audit
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ Five upgrade phases derived from memory philosophy research, each mapped to concrete code changes in the RecallNest codebase. Phase 1 (Emotional Valence in Decay) is implementation-ready today -- the building blocks exist but are not wired into production paths. Phases 2-5 build outward from that foundation. All phases are designed to be independently mergeable and testable.
12
+
13
+ ## Dependency Graph
14
+
15
+ ```
16
+ Phase 1 (Emotion Decay) <- standalone, no dependencies
17
+ Phase 2 (Ethics Layer) <- standalone, no dependencies on Phase 1
18
+ Phase 3 (Narrative) <- standalone, enriches Phase 4 if done first
19
+ Phase 4 (Constructive) <- benefits from Phase 3 (narrative siblings), Phase 1 (emotion salience)
20
+ Phase 5 (Predictive) <- benefits from Phase 4 (reconstruction surface)
21
+ ```
22
+
23
+ Phases 1, 2, and 3 can run in parallel. Phase 4 benefits from all three. Phase 5 builds on Phase 4.
24
+
25
+ ## Time Estimates
26
+
27
+ | Phase | Estimated Effort | Complexity |
28
+ |-------|-----------------|------------|
29
+ | Phase 1 | 3-4 hours | Low -- wiring existing code |
30
+ | Phase 2 | 2-3 days | Medium -- new forget engine + lifecycle unification |
31
+ | Phase 3 | 2-3 days | Medium -- new metadata layer + write/read points |
32
+ | Phase 4 | 3-4 days | High -- LLM pipeline changes + grounding rewrite |
33
+ | Phase 5 | 2-3 days | Medium -- new prediction engine + surface layer |
34
+ | **Total** | **~12-15 days** | |
35
+
36
+ ---
37
+
38
+ ## Phase 1: Emotional Valence in Decay (TODAY)
39
+
40
+ ### Goal
41
+ Wire the existing emotion-adjusted decay functions (`adjustHalfLifeForEmotion`, `computeArousalBoost`) into the production retrieval and evolution scoring paths. Extend `EmotionMetadata` with `salience` and `source` fields. Recalculate emotion on text updates. Backfill emotion on import.
42
+
43
+ ### Current State
44
+ - `emotion-detector.ts` -- keyword heuristic, zero LLM cost, fully tested
45
+ - `decay-engine.ts:99-119` -- `adjustHalfLifeForEmotion()` and `computeArousalBoost()` exist with tests but are NOT called from production code
46
+ - `store.ts:370` -- `detectEmotionIfEnabled()` auto-annotates on `store()`
47
+ - `retriever.ts:261` -- `applyEmotionWeight()` retrieval scoring is live
48
+ - `retriever.ts:1657` -- `applyTimeDecay()` does NOT use emotion-adjusted half-life
49
+ - `memory-evolution.ts:306` -- `computeDecayScore()` has no emotion factor
50
+ - Feature flag: `RECALLNEST_EMOTION_SCORING=true`
51
+
52
+ ### Files Changed
53
+
54
+ | File | Function(s) | Change |
55
+ |------|------------|--------|
56
+ | `src/memory-schema.ts` | `EmotionMetadata`, `EmotionMetadataSchema` | Add `salience` and `source` fields |
57
+ | `src/retriever.ts` | `applyTimeDecay()` (line 1657) | Wire `adjustHalfLifeForEmotion()` into half-life computation |
58
+ | `src/memory-evolution.ts` | `computeDecayScore()` (line 306), weight constants | Add emotion salience factor, rebalance weights |
59
+ | `src/store.ts` | `update()` (line 863) | Recalculate emotion when text changes |
60
+ | `src/store.ts` | `importEntry()` (line 433) | Backfill emotion if absent |
61
+ | `src/emotion-detector.ts` | `detectEmotion()` | Compute `salience` from |valence| + arousal |
62
+ | Tests | New + updated | Emotion-decay integration, update re-detection, import backfill |
63
+
64
+ ### Steps
65
+
66
+ #### 1.1 Extend EmotionMetadata schema
67
+ **File:** `src/memory-schema.ts` (lines 40-53)
68
+
69
+ - [x] Add `salience?: number` field to `EmotionMetadata` interface (0-1 composite of |valence| + arousal)
70
+ - [x] Add `source?: "keyword" | "llm" | "user"` field to `EmotionMetadata` interface
71
+ - [x] Update `EmotionMetadataSchema` Zod object to include `salience: z.number().min(0).max(1).optional()` and `source: z.enum(["keyword", "llm", "user"]).optional()`
72
+ - [x] Verify `parseEmotion()` still works with old data (no salience/source)
73
+ - **Why:** Salience is the single composite signal that decay and evolution scoring will consume. Source enables future LLM-based emotion detection without breaking keyword entries.
74
+ - **Risk:** Low -- additive optional fields, backward compatible
75
+
76
+ #### 1.2 Compute salience in detectEmotion
77
+ **File:** `src/emotion-detector.ts`, function `detectEmotion()` (line 62)
78
+
79
+ - [x] After computing `valence` and `arousal`, compute `salience = clamp((Math.abs(valence) + arousal) / 2, 0, 1)`
80
+ - [x] Add `salience` and `source: "keyword"` to the returned `EmotionMetadata` object
81
+ - [x] Update `detectEmotionIfEnabled()` -- no change needed (passes through)
82
+ - **Why:** Decay engine needs a single 0-1 signal, not separate valence/arousal. Formula: average of emotional intensity and arousal, giving equal weight to "how emotional" and "how urgent."
83
+ - **Risk:** Low -- pure addition to return value
84
+
85
+ #### 1.3 Wire emotion into applyTimeDecay
86
+ **File:** `src/retriever.ts`, method `applyTimeDecay()` (line 1657)
87
+
88
+ - [x] Import `adjustHalfLifeForEmotion` from `decay-engine.ts` and `parseEmotion` from `memory-schema.ts`
89
+ - [x] At line 1670-1672 (after `this.accessTracker.computeEffectiveHalfLife()`), add emotion adjustment: if `isEmotionScoringEnabled()`, parse emotion from `r.entry.metadata`, call `adjustHalfLifeForEmotion(halfLife, emotion)` to get final half-life
90
+ - [x] Guard behind `isEmotionScoringEnabled()` flag check
91
+ - [x] The arousal boost (`computeArousalBoost()`) applies to score before decay
92
+
93
+ **Current code pattern:**
94
+ ```typescript
95
+ const halfLife = this.accessTracker
96
+ ? this.accessTracker.computeEffectiveHalfLife(baseHalfLife, r.entry.metadata)
97
+ : baseHalfLife;
98
+ const tier = resolveTier(r.entry.metadata);
99
+ const factor = weibullDecay(ageDays, halfLife, tier);
100
+ return { ...r, score: clamp01(r.score * factor, r.score * 0.3) };
101
+ ```
102
+
103
+ **Target code:**
104
+ ```typescript
105
+ let halfLife = this.accessTracker
106
+ ? this.accessTracker.computeEffectiveHalfLife(baseHalfLife, r.entry.metadata)
107
+ : baseHalfLife;
108
+ // HP-emo: Emotion-adjusted half-life — strong emotion slows forgetting
109
+ const emotion = isEmotionScoringEnabled() ? parseEmotion(r.entry.metadata) : null;
110
+ if (emotion) {
111
+ halfLife = adjustHalfLifeForEmotion(halfLife, emotion);
112
+ }
113
+ const tier = resolveTier(r.entry.metadata);
114
+ const factor = weibullDecay(ageDays, halfLife, tier);
115
+ const arousalFactor = emotion ? computeArousalBoost(emotion) : 1.0;
116
+ return { ...r, score: clamp01(r.score * arousalFactor * factor, r.score * 0.3) };
117
+ ```
118
+
119
+ - **Why:** This is the primary gap -- emotion detection runs on write, emotion weight runs on retrieval scoring, but the decay curve itself ignores emotion. A frustrating debugging session should decay slower than a neutral log entry.
120
+ - **Risk:** Medium -- touches the core scoring pipeline. Parse emotion once per result to avoid double-parse overhead.
121
+
122
+ #### 1.4 Add emotion factor to computeDecayScore
123
+ **File:** `src/memory-evolution.ts`, function `computeDecayScore()` (line 306), constants at lines 292-295
124
+
125
+ - [x] Import `parseEmotion`, `isEmotionScoringEnabled` from `memory-schema.ts`
126
+ - [x] Add optional `metadata?: string` parameter to `computeDecayScore()` (backward compatible)
127
+ - [x] When `isEmotionScoringEnabled()` and metadata is provided, extract salience
128
+ - [x] Rebalance weights from `0.2/0.3/0.5` to `0.15/0.25/0.45/0.15`:
129
+ ```typescript
130
+ const EMOTION_WEIGHT = isEmotionScoringEnabled() ? 0.15 : 0;
131
+ const TIME_W = isEmotionScoringEnabled() ? 0.15 : 0.2;
132
+ const FREQ_W = isEmotionScoringEnabled() ? 0.25 : 0.3;
133
+ const IMP_W = isEmotionScoringEnabled() ? 0.45 : 0.5;
134
+ // Final: TIME_W * timeDecay + FREQ_W * frequencyScore + IMP_W * importance + EMOTION_WEIGHT * salience
135
+ ```
136
+ - [x] Update all callers to pass metadata where available:
137
+ - `auto-gc.ts:142` -- pass `entry.metadata`
138
+ - `retriever.ts:1712` (applyEvolutionDecayBlend) -- pass `r.entry.metadata`
139
+ - **Why:** The evolution decay score determines GC archival and evolution blend. Without emotion, high-emotion events get same decay as routine logs.
140
+ - **Risk:** Medium -- changes GC archival threshold behavior. Emotional memories will resist archival slightly more (desirable).
141
+
142
+ #### 1.5 Re-detect emotion on text update
143
+ **File:** `src/store.ts`, method `update()` (line 863)
144
+
145
+ - [x] After building the `updated` entry, if `updates.text` is provided (text changed), re-run emotion detection:
146
+ ```typescript
147
+ if (updates.text) {
148
+ const emotionResult = detectEmotionIfEnabled(updated.text);
149
+ if (emotionResult) {
150
+ const meta = JSON.parse(updated.metadata || "{}");
151
+ meta.emotion = emotionResult;
152
+ updated.metadata = JSON.stringify(meta);
153
+ }
154
+ }
155
+ ```
156
+ - [x] Place this BEFORE the `table!.delete` + `table!.add` at lines 918-920
157
+ - **Why:** When text is updated (e.g., supersede-with-edit), stale emotion metadata creates scoring inaccuracy.
158
+ - **Risk:** Low -- only fires when text is explicitly changed
159
+
160
+ #### 1.6 Backfill emotion on importEntry
161
+ **File:** `src/store.ts`, method `importEntry()` (line 433)
162
+
163
+ - [x] After building `full` entry, check if emotion is already present in metadata. If not, detect and inject:
164
+ ```typescript
165
+ const metaParsed = JSON.parse(full.metadata || "{}");
166
+ if (!metaParsed.emotion) {
167
+ const emotionResult = detectEmotionIfEnabled(full.text);
168
+ if (emotionResult) {
169
+ metaParsed.emotion = emotionResult;
170
+ full.metadata = JSON.stringify(metaParsed);
171
+ }
172
+ }
173
+ ```
174
+ - **Why:** Imported entries may pre-date emotion detection. Backfilling ensures all entries have emotion metadata.
175
+ - **Risk:** Low -- only adds when absent, never overwrites
176
+
177
+ #### 1.7 Tests
178
+ - [x] **emotion-decay.test.ts**: Add test that `salience` is present in `detectEmotion()` output and matches expected formula
179
+ - [x] **emotion-decay.test.ts**: Add test that `source` field is `"keyword"` in `detectEmotion()` output
180
+ - [x] **New: emotion-evolution-decay.test.ts**: Test `computeDecayScore` with emotion metadata yields higher score than without
181
+ - [x] **New: emotion-evolution-decay.test.ts**: Test backward compat -- no metadata param still works
182
+ - [x] **New: emotion-evolution-decay.test.ts**: Test weight rebalancing (weights sum to 1.0)
183
+ - [x] **Extend retriever tests**: Two entries, same age/importance/frequency, different emotion -- emotional one scores higher after `applyTimeDecay`
184
+ - [x] **Store tests**: Verify `update()` with text change re-detects emotion
185
+ - [x] **Store tests**: Verify `importEntry()` backfills emotion when absent, preserves when present
186
+ - [x] Run `bun test` -- all 1000+ tests must pass
187
+
188
+ ### Verification
189
+ 1. `bun test` -- full green, baseline maintained
190
+ 2. Manual check: store emotional memory, retrieve at simulated t=30d, confirm higher score vs neutral
191
+ 3. Check `computeDecayScore` output for emotional vs neutral with same evo params
192
+
193
+ ### Risks for Phase 1
194
+ - **Double-parse overhead in applyTimeDecay**: Parse emotion once per result, pass to both functions
195
+ - **Weight rebalancing changes GC behavior**: Test with existing GC tests
196
+ - **Feature flag dependency**: All changes gated by `RECALLNEST_EMOTION_SCORING=true`. Flag off = identical to current
197
+
198
+ ---
199
+
200
+ ## Phase 2: Memory Ethics Layer
201
+
202
+ ### Goal
203
+ Add privacy tiers, build a proper forget engine with cascade deletion, unify lifecycle state inconsistency, expose `forget_memory` MCP tool with audit logging.
204
+
205
+ ### Current State
206
+ - `cascade-forget.ts:91` reads legacy `meta.state` -- inconsistent with `evolution.status`
207
+ - `consolidation-engine.ts:71-77` reads BOTH `isActiveMemory()` and `meta.state`
208
+ - `audit-log.ts` exists but lacks "forget" operation type
209
+ - `KGStore.deleteBySource()` exists at `kg-store.ts:314` but never called from forget path
210
+ - No privacy tier field exists
211
+
212
+ ### Files Changed
213
+
214
+ | File | Function(s) | Change |
215
+ |------|------------|--------|
216
+ | `src/memory-schema.ts` | New types | `PrivacyTier` type + Zod schema |
217
+ | `src/forget-engine.ts` | **New file** | Full cascade forget with KG cleanup, audit, privacy checks |
218
+ | `src/audit-log.ts` | `AuditOperation` type | Add `"forget"` and `"cascade_forget"` operations |
219
+ | `src/cascade-forget.ts` | `cascadeForget()` | Migrate `meta.state` to `evolution.status` |
220
+ | `src/consolidation-engine.ts` | `isActive()` | Remove legacy `meta.state` double-check |
221
+ | `src/capture-engine.ts` | KG extraction gate | Privacy tier check before KG extraction |
222
+ | `src/mcp-server.ts` | New tool | `forget_memory` MCP tool |
223
+ | `src/store.ts` | `store()`, `StoreMemoryInput` | Accept optional `privacyTier` |
224
+ | Tests | New | forget-engine, privacy-tier, lifecycle-state tests |
225
+
226
+ ### Steps
227
+
228
+ #### 2.1 Add PrivacyTier to schema
229
+ - [x] `PrivacyTier` type: `"ephemeral" | "private" | "durable" | "shared"`
230
+ - [x] `PrivacyTierSchema` Zod enum
231
+ - [x] Optional `privacyTier` in `StoreMemoryInputSchema`
232
+
233
+ #### 2.2 Unify lifecycle state
234
+ - [x] `cascade-forget.ts:91` -- replace `meta.state` with `isActiveMemory()`
235
+ - [x] `consolidation-engine.ts:70-77` -- simplify to only `isActiveMemory()`
236
+ - [x] Grep for any other `meta.state` reads, migrate them all
237
+
238
+ #### 2.3 Extend audit operations
239
+ - [x] Add `"forget"` and `"cascade_forget"` to `AuditOperation` union
240
+
241
+ #### 2.4 Build forget engine (NEW: `src/forget-engine.ts`)
242
+ - [x] `forgetMemory()`: fetch → privacy check → evidence export → KG delete → pin archive → cascade → primary delete → audit
243
+ - [x] `forgetByScope()` for bulk scope-level forget
244
+ - [x] Confirmation gate for `"durable"` entries
245
+
246
+ #### 2.5 Privacy tier gate in capture
247
+ - [x] Before KG extraction, skip if `privacyTier === "ephemeral" | "private"`
248
+
249
+ #### 2.6 Register `forget_memory` MCP tool
250
+ - [x] Schema: `{ memoryId: string, confirm: boolean, reason?: string }`
251
+ - [x] Returns deletion summary + evidence paths
252
+
253
+ #### 2.7 Tests
254
+ - [x] forget-engine: KG cleanup, cascade demotion, audit trail
255
+ - [x] Privacy tier blocks without confirm
256
+ - [x] Evidence export for high-importance memories
257
+ - [x] Consolidation/cascade-forget regression with new `isActiveMemory()`
258
+ - [x] `bun test` full green
259
+
260
+ ### Verification
261
+ 1. Store memory → extract KG → forget → verify KG triples gone
262
+ 2. Audit log shows complete forget trail
263
+ 3. `"ephemeral"` memory produces no KG triples
264
+
265
+ ---
266
+
267
+ ## Phase 3: Autobiographical Narrative Architecture
268
+
269
+ ### Goal
270
+ Add orthogonal narrative metadata layer (life-period / general-event / specific-event) on top of existing 6 categories. Enable temporal grouping and narrative retrieval without disrupting category logic.
271
+
272
+ ### Current State
273
+ - 6 categories hardcoded across `memory-schema.ts`, `memory-boundaries.ts`, `retriever.ts`, `context-composer.ts` -- **do not modify**
274
+ - `event-segmenter.ts:105` has `segmentEvents()`, `temporal-parser.ts:15` has date parsing
275
+
276
+ ### Files Changed
277
+
278
+ | File | Function(s) | Change |
279
+ |------|------------|--------|
280
+ | `src/narrative-schema.ts` | **New file** | Narrative metadata types + Zod schemas |
281
+ | `src/narrative-tagger.ts` | **New file** | Rule-based narrative period/event assignment |
282
+ | `src/capture-engine.ts` | `buildStructuredMetadata()` | Inject narrative metadata |
283
+ | `src/ingest.ts` | `buildIngestedEntry()` | Inject narrative metadata |
284
+ | `src/retriever.ts` | New method | `expandNarrativeSiblings()` |
285
+ | `src/context-composer.ts` | Result rendering | Group by narrative period |
286
+ | `src/memory-output.ts` | Rendering | Narrative period labels |
287
+ | `src/graph-export.ts` | Graph edges | Narrative relationship edges |
288
+
289
+ ### Steps
290
+ - [x] 3.1 Define `NarrativeMetadata` interface: `lifePeriodId/Label`, `generalEventId/Label`, `specificEventId/Label`, `startAt`, `endAt`, `sequence`
291
+ - [x] 3.2 Build rule-based narrative tagger (scope prefix, temporal clustering, keyword signals)
292
+ - [x] 3.3 Inject narrative at write points (`capture-engine.ts:140`, `ingest.ts:662`)
293
+ - [x] 3.4 `expandNarrativeSiblings()` in retriever: pull entries sharing same `generalEventId`
294
+ - [x] 3.5 Rendering updates: group by `lifePeriodLabel`, narrative edges in graph export
295
+ - [x] 3.6 Tests + `bun test` full green
296
+
297
+ ---
298
+
299
+ ## Phase 4: Constructive Retrieval
300
+
301
+ ### Goal
302
+ Replace "top-k then summarize" with multi-source candidate expansion + grounded reconstruction. Return reconstruction as first-class object.
303
+
304
+ ### Current State
305
+ - `context-reconstructor.ts:91` exists, `retriever.ts:706` `runReconstruction()` exists
306
+ - `mcp-server.ts:888` supports `reconstruct=true`
307
+ - But: only top-k summarize, `metadata._reconstruction` is hacky, `computeCoverage()` is lexical
308
+
309
+ ### Steps
310
+ - [x] 4.1 Define `ReconstructionOutput` with sources, confidence, contradictions
311
+ - [x] 4.2 Add candidate expansion: KG neighbors + evolution chains + cluster members + narrative siblings
312
+ - [x] 4.3 Replace lexical coverage with source-map grounding + contradiction detection
313
+ - [x] 4.4 Return reconstruction as first-class object (remove `metadata._reconstruction` hack)
314
+ - [x] 4.5 Update MCP rendering for new output shape
315
+ - [x] 4.6 Pass checkpoint context (openLoops, nextActions) into reconstruction prompt
316
+ - [x] 4.7 Tests + `bun test` full green
317
+
318
+ ---
319
+
320
+ ## Phase 5: Predictive Prospective Memory
321
+
322
+ ### Goal
323
+ Evolve `prospective-memory.ts` from explicit reminders to include pattern-predicted suggestions. Surface behavioral insights as ephemeral predicted reminders.
324
+
325
+ ### Current State
326
+ - `prospective-memory.ts` -- `setReminder()`, `checkTriggers()`, `fireReminder()` explicit only
327
+ - Behavioral signals: checkpoint openLoops, workflow observations, frequency tracker, access tracker, topic tags
328
+
329
+ ### Steps
330
+ - [x] 5.1 Extend `ProspectiveMetadata` with `source`, `confidence`, `evidence`, `lastSuggestedAt`, `acceptedAt`
331
+ - [x] 5.2 Build `prediction-engine.ts`: collect signals, score predictions, threshold at confidence >= 0.6
332
+ - [x] 5.3 `suggestPredictedReminders()`: deduplicate vs existing, store as ephemeral, auto-expire 7d
333
+ - [x] 5.4 Surface in `search_memory` as separate "Suggested Reminders" section
334
+ - [x] 5.5 Acceptance/promotion flow: repeated/accepted → promote to explicit; ignored → demote
335
+ - [x] 5.6 Tests + `bun test` full green
336
+
337
+ ---
338
+
339
+ ## Global Risks & Mitigations
340
+
341
+ | Risk | Impact | Mitigation |
342
+ |------|--------|-----------|
343
+ | Feature flag proliferation | Complexity | Phase 1-2 reuse `RECALLNEST_EMOTION_SCORING`. Phase 3-5 add `RECALLNEST_NARRATIVE_MODE` and `RECALLNEST_PREDICTIVE_MEMORY` |
344
+ | Metadata JSON bloat | Storage + parse cost | Each phase adds ~100 bytes/entry. Total metadata stays under 2KB |
345
+ | Scoring formula instability | Retrieval quality regression | Every weight change gated by feature flag and tested against eval cases |
346
+ | LLM cost creep (Phase 4-5) | Token spend | Phase 1-3 zero LLM. Phase 4 reuses existing reconstruction budget. Phase 5 zero LLM (heuristic) |
347
+
348
+ ---
349
+
350
+ ## Success Criteria
351
+
352
+ - [x] Phase 1: Emotional memories decay 20-30% slower than neutral memories of same age
353
+ - [x] Phase 1: `computeDecayScore` with emotion gives higher scores for high-salience memories
354
+ - [x] Phase 1: All existing tests pass, emotion-decay tests extended
355
+ - [x] Phase 2: `forget_memory` MCP tool deletes entry + KG triples + cascade demotes + audit logs
356
+ - [x] Phase 2: No more `meta.state` reads anywhere in codebase (all `evolution.status`)
357
+ - [x] Phase 3: Ingested sessions have narrative metadata with period/event IDs
358
+ - [x] Phase 3: Search returns narrative siblings alongside direct matches
359
+ - [x] Phase 4: Reconstruction uses expanded candidate set (KG + evolution + cluster)
360
+ - [x] Phase 4: Reconstruction is first-class return object, not metadata hack
361
+ - [x] Phase 5: Predicted reminders surface from behavioral signals
362
+ - [x] Phase 5: Predictions auto-expire and respect acceptance/rejection
363
+ - [x] All phases: `bun test` passes with baseline maintained or increased
@@ -0,0 +1,155 @@
1
+ # RecallNest 产品化升级实施计划
2
+
3
+ ## 目标
4
+
5
+ 把 RecallNest 从"引擎很强但用户无感"变成"用户能看到记忆在成长"。
6
+
7
+ ## 四个升级(按优先级)
8
+
9
+ ---
10
+
11
+ ### Upgrade 1: SKILL.md — 记忆伙伴使用协议
12
+
13
+ **做什么**:在 repo 根目录创建 `SKILL.md`,教 LLM 怎么当好记忆伙伴(不只是 API 文档)。
14
+
15
+ **内容结构**:
16
+ - Session Protocol(resume → work → checkpoint)
17
+ - Onboarding Flow(首次检测:memory_stats 为 0 时引导)
18
+ - Memory Feedback(store 后回显,search 后展示匹配)
19
+ - Tool Decision Tree(什么时候 store vs checkpoint vs pin)
20
+ - Weekly Health Check(data_checkup + dream + lint)
21
+
22
+ **改动**:
23
+ - 新建 `SKILL.md`(~250 行 markdown)
24
+ - 零代码改动
25
+
26
+ **风险**:低。纯文档。
27
+
28
+ ---
29
+
30
+ ### Upgrade 2: Memory Lint — 记忆体检
31
+
32
+ **做什么**:新增 MCP tool + CLI 命令,对记忆做质量自检。
33
+
34
+ **检查项**:
35
+ | 检查 | 复用什么 | 怎么做 |
36
+ |------|---------|--------|
37
+ | 矛盾检测 | `detectHeuristicContradiction`(consolidation-engine.ts) | 同 scope 同 category 内两两比较 |
38
+ | 重复检测 | `cosineSimilarity`(multi-vector.ts) | 向量相似度 > 0.92 的配对 |
39
+ | 陈旧检测 | `parseEvolution` 的 accessCount/lastAccessedAt | 90 天未被召回 + accessCount ≤ 1 |
40
+ | 孤儿检测 | consolidatedInto 指向的 ID | 不存在的引用 |
41
+ | 健康分 | 综合以上 | 100 - (矛盾×10 + 重复×5 + 陈旧×2 + 孤儿×3) |
42
+
43
+ **改动**:
44
+ - 新建 `src/memory-lint.ts`(~180 行)
45
+ - 新建 `src/__tests__/memory-lint.test.ts`(~150 行)
46
+ - 改 `src/mcp-server.ts`:注册 `memory_lint` tool(~25 行)
47
+ - 改 `src/cli.ts`:增加 `lint` 命令(~30 行)
48
+ - 改 `src/api-server.ts`:增加 `GET /v1/lint`(~20 行)
49
+ - 改 `src/consolidation-engine.ts`:export `detectHeuristicContradiction`(1 行)
50
+ - 改 `package.json`:增加 `lint:memory` 脚本
51
+
52
+ **性能守卫**:矛盾/重复检测是 O(n²),每个 scope/category 组限 200 条,按 importance 降序取。
53
+
54
+ **预计**:~405 LOC
55
+
56
+ **风险**:中。矛盾检测基于启发式,会有误报,报告中标注"启发式建议"。
57
+
58
+ ---
59
+
60
+ ### Upgrade 3: Dashboard 仪表盘首页
61
+
62
+ **做什么**:现有 UI(port 4317)只有搜索,加一个 Dashboard 首页 tab。
63
+
64
+ **展示内容**:
65
+ - 总记忆数 + 分类分布(bar chart,纯 CSS 实现,不引入图表库)
66
+ - 本周/本月新增趋势
67
+ - 健康分(来自 Lint)
68
+ - 最近召回 Top N
69
+ - 沉睡记忆列表(90天未召回)
70
+
71
+ **改动**:
72
+ - 改 `src/ui-server.ts`:新增 4 个 API endpoint(~80 行)
73
+ - `GET /api/dashboard-stats`
74
+ - `GET /api/recent-recalls`
75
+ - `GET /api/lint-summary`
76
+ - `GET /api/stale-memories`
77
+ - 改 `assets/ui/index.html`:增加 Dashboard tab(~60 行)
78
+ - 改 `assets/ui/app.js`:Dashboard 渲染逻辑(~120 行)
79
+ - 改 `assets/ui/styles.css`:Dashboard 样式(~80 行)
80
+
81
+ **依赖**:Upgrade 2(Lint 的健康分)。可以先不接 Lint,用 placeholder。
82
+
83
+ **预计**:~340 LOC
84
+
85
+ **风险**:中。Growth trend 需要扫全量 entries 按时间分桶,大库可能慢。缓存 60s 缓解。
86
+
87
+ ---
88
+
89
+ ### Upgrade 4: HTML 知识图谱
90
+
91
+ **做什么**:把记忆导出为一个交互式 HTML 文件,浏览器打开即可看到力导向图。
92
+
93
+ **节点/边设计**:
94
+ - 节点 = 每条记忆,颜色按 category(profile=金, preferences=蓝, entities=绿, events=灰, cases=红, patterns=紫)
95
+ - 边 = scope 关系 + consolidation 链 + supersede 链 + cluster 关系
96
+ - 支持缩放、拖拽、点击查看详情
97
+
98
+ **改动**:
99
+ - 新建 `src/graph-export.ts`(~250 行)
100
+ - `buildMemoryGraph()`:从 store 构建节点/边
101
+ - `renderGraphHTML()`:生成自包含 HTML(D3.js via CDN,或 `--offline` 内嵌)
102
+ - `exportMemoryGraph()`:写文件到 `data/exports/`
103
+ - 新建 `src/__tests__/graph-export.test.ts`(~100 行)
104
+ - 改 `src/mcp-server.ts`:注册 `export_graph` tool(~25 行)
105
+ - 改 `src/cli.ts`:增加 `graph` 命令(~25 行)
106
+ - 改 `src/ui-server.ts`:增加 `/api/graph` endpoint(~20 行)
107
+
108
+ **默认上限**:200 节点(按 importance 降序),避免图谱变成乱麻。
109
+
110
+ **预计**:~420 LOC
111
+
112
+ **风险**:中高。>100 节点时力导向布局可能卡顿。限制节点数+scope 过滤。
113
+
114
+ ---
115
+
116
+ ## 实施顺序
117
+
118
+ ```
119
+ Phase 1(可独立合并)
120
+ ├── 1.1 SKILL.md(无代码依赖)
121
+ └── 1.2 memory-lint.ts + tests
122
+
123
+ Phase 2(依赖 1.2)
124
+ ├── 2.1 Lint MCP tool + CLI
125
+ └── 2.2 Lint API endpoint
126
+
127
+ Phase 3(依赖 Phase 2)
128
+ └── 3.1 Dashboard 首页
129
+
130
+ Phase 4(独立,可与 1-3 并行)
131
+ └── 4.1 HTML 知识图谱
132
+ ```
133
+
134
+ **可并行**:SKILL.md 和 Graph Export 不依赖其他升级。
135
+
136
+ ## 总改动量
137
+
138
+ | 升级 | 新文件 | 改动文件 | 新增 LOC |
139
+ |------|--------|---------|---------|
140
+ | SKILL.md | 1 | 0 | ~250 (markdown) |
141
+ | Memory Lint | 2 | 4 | ~405 |
142
+ | Dashboard | 0 | 4 | ~340 |
143
+ | Knowledge Graph | 2 | 3 | ~420 |
144
+ | **总计** | **5** | **~8** | **~1,415** |
145
+
146
+ ## 成功标准
147
+
148
+ - [ ] SKILL.md 覆盖 5 个核心场景
149
+ - [ ] `memory_lint` MCP tool 返回矛盾/重复/陈旧/孤儿数量 + 0-100 健康分
150
+ - [ ] `bun run src/cli.ts lint` CLI 输出相同报告
151
+ - [ ] Dashboard tab 展示总记忆数、分类分布、健康分、沉睡记忆
152
+ - [ ] `bun run src/cli.ts graph` 生成有效 HTML,浏览器打开有交互图谱
153
+ - [ ] 现有 1168 个测试全部通过
154
+ - [ ] 新增测试 > 20 个
155
+ - [ ] 不引入新 npm 依赖
package/plan.md ADDED
@@ -0,0 +1,140 @@
1
+ # RecallNest 架构迭代 Plan
2
+
3
+ **目标**:提升 RecallNest 架构能力(不刷分,关注真实场景)
4
+ **推送**:全部推河马仓(origin),实用的推公开仓(public)
5
+
6
+ ---
7
+
8
+ ## 已完成
9
+
10
+ ### Step 1: 隐式偏好检测 (preference-slots.ts)
11
+ - `ImplicitUsagePreferenceSlot` + 8 种中英文模式 | `6db35d08`
12
+
13
+ ### Step 2: Source Diversity (retriever.ts)
14
+ - `sourceDiversity` config, round-robin 跨 scope | `6db35d08`
15
+
16
+ ### Step 3: Adaptive Candidate Pool (retriever.ts)
17
+ - `adaptivePoolMultiplier` config, 聚合 query 自动扩池 | `6db35d08`
18
+
19
+ ### Step 4: 隐式偏好双写 (capture-engine.ts)
20
+ - 非 preference 记忆自动派生 preference 副本 | `778cd906`
21
+
22
+ ### Step 5: Multi-hop 两轮检索 (retriever.ts)
23
+ - 实体驱动的两轮检索 + 并行 follow-up | `86a4f0a3`
24
+
25
+ ---
26
+
27
+ ## 待做:架构改进(借鉴 memL + 自身短板)
28
+
29
+ ### Step 7: Recall Governor — auto-recall 治理层 [高优先级]
30
+
31
+ **问题**:当前 auto-recall 路径只有 `shouldSkipRetrieval()` (gating) 和 `noise-filter` (事后过滤)。缺少中间治理层:
32
+ - 无字符预算控制,注入过多记忆浪费 token
33
+ - 无会话去重,同一轮对话里同一条记忆可能被多次注入
34
+ - 无状态过滤二次确认(retriever 有 evolution filter 但 auto-recall 路径没有兜底)
35
+
36
+ **方案**:新增 `recall-governor.ts`,在 auto-recall.ts 的 retrieve 后、返回前插入 6 层治理:
37
+ 1. Gating — 已有 `shouldSkipRetrieval()`,保持
38
+ 2. Query 截断 — 超长 query 截断到 1000 chars(省 embedding 成本)
39
+ 3. 检索 — 已有 HybridRetriever,保持
40
+ 4. 状态过滤 — 排除 archived/superseded, tier=peripheral
41
+ 5. 预算控制 — 总字符预算 + 条目数上限(可配置)
42
+ 6. 会话去重 — 维护本轮已注入 ID set,不重复注入
43
+
44
+ **文件**:`src/recall-governor.ts`(新增), `src/auto-recall.ts`(接入)
45
+ **工作量**:约 120 行 + 测试
46
+
47
+ ---
48
+
49
+ ### Step 8: Admission Control — 写入准入控制 [高优先级]
50
+
51
+ **问题**:当前记忆写入只有 noise-filter(检索时才过滤)和 write-verifier(写后验证),没有写前门控。低质量记忆进库再过滤不如一开始就拦。
52
+
53
+ **方案**:新增 `admission-control.ts`,在 capture-engine.ts 的 persistMemory 入口加准入检查:
54
+ - 最短文本长度(< 10 chars 拒绝)
55
+ - noise-filter 前置(写入前就过滤)
56
+ - importance 下限(< 0.2 拒绝或标记 pending_review)
57
+ - 重复检测(向量相似度 > 0.95 近似重复直接跳过)
58
+ - 每 scope 写入频率限制(防 flood)
59
+
60
+ **文件**:`src/admission-control.ts`(新增), `src/capture-engine.ts`(接入)
61
+ **工作量**:约 80 行 + 测试
62
+
63
+ ---
64
+
65
+ ### Step 9: LLM 降级策略 — dream-pipeline + capture 容错 [中优先级]
66
+
67
+ **问题**:dream-pipeline 和 LLM importance assessment 依赖外部 LLM。LLM 挂了:
68
+ - dream-pipeline 直接失败
69
+ - importance 静默跳过(fallback 默认 0.7,还行)
70
+ - capture-heuristic 是纯 regex 备选但不在同一链路
71
+
72
+ **方案**:借鉴 memL 三层降级:
73
+ - Level 1: 正常 LLM 调用
74
+ - Level 2: 降级 prompt(更短,更高成功率)
75
+ - Level 3: 确定性 fallback(零 LLM)
76
+ - dream consolidation: 跳过本轮下次再试
77
+ - importance: heuristic 规则(文本长度 + category 权重)
78
+ - capture: capture-heuristic.ts 的 regex 提取
79
+
80
+ **文件**:`src/llm-client.ts`(加 degradation wrapper)
81
+ **工作量**:约 60 行 + 测试
82
+
83
+ ---
84
+
85
+ ### Step 10: Metadata 显式字段 — 去 JSON bag [中优先级,大工作量]
86
+
87
+ **问题**:85 处 `JSON.parse(metadata)`。metadata 是 JSON string,塞了 evolution/boundary/anchor/preferenceSlot/pii/tags 等所有东西。每次读都要 parse,LanceDB filter 不能直接用内部字段。
88
+
89
+ **方案**:高频字段提升为 MemoryEntry 顶级字段:
90
+ ```
91
+ 新增:anchor, evolution_status, confidence, access_count,
92
+ last_accessed_at, supersedes, superseded_by, tier
93
+ 保留 metadata:低频扩展字段仍用 JSON string
94
+ ```
95
+
96
+ **风险**:LanceDB schema 变更需重建表,需 migration 脚本。
97
+ **工作量**:约 300 行 + migration + 大量测试更新
98
+ **建议**:单独一个完整 session,不混其他改动
99
+
100
+ ---
101
+
102
+ ### Step 11: 两段式捕获 — message buffer + batch persist [低优先级]
103
+
104
+ **问题**:capture-engine 单条同步写入。高频对话产生碎片记忆。
105
+
106
+ **方案**:借鉴 memL 两段式:
107
+ - Stage 1: message_received -> 缓存到内存 buffer
108
+ - Stage 2: agent_end(或 buffer 满/超时)-> 批量合并后写入
109
+
110
+ **价值**:减少碎片,LLM 看到更完整上下文提高提取质量
111
+ **工作量**:约 150 行 + event bus 集成
112
+ **依赖**:host adapter 需提供 message_received / agent_end 事件
113
+
114
+ ---
115
+
116
+ ## 停车场(不做)
117
+
118
+ | 方案 | 原因 |
119
+ |------|------|
120
+ | Extraction prompt 加偏好示例 | 针对题型优化 = 刷分 |
121
+ | Reader 升级 | 不是 RecallNest 自身能力 |
122
+ | L1 Context Engine | memL 的差异化,RecallNest 定位是 L2 记忆引擎 |
123
+ | Embedding 模型更换 | 当前够用 |
124
+
125
+ ---
126
+
127
+ ## 优先级排序
128
+
129
+ ```
130
+ 高优先级(下一个窗口做)
131
+ Step 7: Recall Governor ~120 行 <- 生产环境最需要
132
+ Step 8: Admission Control ~80 行 <- 防垃圾记忆入库
133
+
134
+ 中优先级(之后做)
135
+ Step 9: LLM 降级策略 ~60 行 <- 生产容错
136
+ Step 10: Metadata 显式字段 ~300 行 <- 大重构,单独 session
137
+
138
+ 低优先级(有空再做)
139
+ Step 11: 两段式捕获 ~150 行 <- 需 host adapter 配合
140
+ ```