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.
- package/.dockerignore +11 -0
- package/CLAUDE.md +7 -3
- package/Dockerfile +20 -0
- package/README.md +202 -130
- package/README_CN.md +263 -190
- package/ROADMAP.md +128 -81
- package/SKILL.md +250 -0
- package/assets/dashboard.png +0 -0
- package/assets/knowledge-graph.png +0 -0
- package/assets/screenshots/ui-full.png +0 -0
- package/assets/screenshots/ui-overview.png +0 -0
- package/assets/screenshots/ui-search.png +0 -0
- package/assets/ui/app.js +227 -3
- package/assets/ui/index.html +81 -11
- package/assets/ui/styles.css +178 -2
- package/bin/recallnest.mjs +62 -0
- package/bun.lock +337 -0
- package/daily-recall.sh +23 -0
- package/daily-reflections/2026-04-02.md +35 -0
- package/daily-reflections/2026-04-03.md +37 -0
- package/daily-reflections/2026-04-04.md +37 -0
- package/daily-reflections/2026-04-05.md +35 -0
- package/daily-reflections/2026-04-06.md +36 -0
- package/daily-reflections/2026-04-07.md +35 -0
- package/daily-reflections/2026-04-08.md +35 -0
- package/daily-reflections/2026-04-12.md +35 -0
- package/data/alias-map.example.json +22 -0
- package/docs/plans/2026-04-01-recall-boost.md +100 -0
- package/docs/superpowers/plans/2026-04-11-constructive-retrieval-emotion.md +1195 -0
- package/docs/superpowers/specs/2026-04-11-constructive-retrieval-emotion-design.md +398 -0
- package/glama.json +6 -0
- package/integrations/setup-all.sh +31 -0
- package/package.json +10 -3
- package/plan-philosophy-upgrades.md +363 -0
- package/plan-product-upgrades.md +155 -0
- package/plan.md +140 -0
- package/research-memory-evolution.md +440 -0
- package/research-rl-agent-memory.md +330 -0
- package/scripts/backfill-anchors.ts +130 -0
- package/scripts/diagnose-vector-search.ts +132 -0
- package/scripts/migrate-language.ts +75 -0
- package/scripts/verify-emotion-decay.ts +148 -0
- package/src/__tests__/activity-counter.test.ts +96 -0
- package/src/__tests__/admission-control.test.ts +206 -0
- package/src/__tests__/anchor-generator.test.ts +67 -0
- package/src/__tests__/babel-integration.test.ts +139 -0
- package/src/__tests__/capture-engine.test.ts +5 -3
- package/src/__tests__/cascade-forget.test.ts +1 -1
- package/src/__tests__/cc8-cc1-essential-context.test.ts +298 -0
- package/src/__tests__/circuit-breaker.test.ts +99 -0
- package/src/__tests__/cluster-consolidation.test.ts +712 -0
- package/src/__tests__/confidence-metadata.test.ts +125 -0
- package/src/__tests__/confidence-tracker.test.ts +53 -0
- package/src/__tests__/consolidation-engine.test.ts +1 -1
- package/src/__tests__/constructive-retrieval.test.ts +509 -0
- package/src/__tests__/context-collapse-renderer.test.ts +214 -0
- package/src/__tests__/context-reconstructor.test.ts +65 -0
- package/src/__tests__/conversation-importer.test.ts +692 -0
- package/src/__tests__/data-checkup.test.ts +225 -0
- package/src/__tests__/decay-exemption.test.ts +84 -0
- package/src/__tests__/dedup-guard.test.ts +39 -0
- package/src/__tests__/distill-lock.test.ts +176 -0
- package/src/__tests__/dream-pipeline.test.ts +232 -0
- package/src/__tests__/emotion-decay.test.ts +46 -0
- package/src/__tests__/emotion-detector.test.ts +56 -0
- package/src/__tests__/emotion-phase1-integration.test.ts +153 -0
- package/src/__tests__/emotion-retrieval.test.ts +56 -0
- package/src/__tests__/ethics-layer.test.ts +476 -0
- package/src/__tests__/feature-flags-integration.test.ts +54 -0
- package/src/__tests__/frequency-tracker.test.ts +113 -0
- package/src/__tests__/graph-export.test.ts +489 -0
- package/src/__tests__/interference-detector.test.ts +203 -0
- package/src/__tests__/mcp-efficiency.test.ts +288 -0
- package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +243 -0
- package/src/__tests__/memory-evolution.test.ts +271 -0
- package/src/__tests__/memory-lint.test.ts +271 -0
- package/src/__tests__/narrative-architecture.test.ts +456 -0
- package/src/__tests__/pending-review.test.ts +77 -0
- package/src/__tests__/post-retrieval-filter.test.ts +178 -0
- package/src/__tests__/prediction-engine.test.ts +349 -0
- package/src/__tests__/predictive-prospective-memory.test.ts +416 -0
- package/src/__tests__/preference-slots.test.ts +99 -0
- package/src/__tests__/query-expander-alias.test.ts +89 -0
- package/src/__tests__/recall-governor.test.ts +227 -0
- package/src/__tests__/resume-context.test.ts +156 -1
- package/src/__tests__/salience-scoring.test.ts +106 -0
- package/src/__tests__/session-distiller.test.ts +611 -0
- package/src/__tests__/skill-engine.test.ts +546 -0
- package/src/__tests__/skill-promotion.test.ts +349 -0
- package/src/__tests__/supersede-chain.test.ts +154 -0
- package/src/__tests__/temporal-validity.test.ts +160 -0
- package/src/__tests__/topic-tag.test.ts +113 -0
- package/src/__tests__/trusted-memory.test.ts +828 -0
- package/src/__tests__/write-verifier.test.ts +122 -0
- package/src/access-tracker.ts +3 -2
- package/src/activity-counter.ts +109 -0
- package/src/adaptive-retrieval.ts +6 -5
- package/src/admission-control.ts +163 -0
- package/src/anchor-generator.ts +80 -0
- package/src/api-server.ts +10 -0
- package/src/audit-log.ts +122 -0
- package/src/auto-gc.ts +98 -29
- package/src/auto-recall.ts +33 -4
- package/src/capture-engine.ts +372 -23
- package/src/cascade-forget.ts +3 -2
- package/src/cli.ts +107 -5
- package/src/compat.ts +22 -0
- package/src/confidence-tracker.ts +210 -47
- package/src/conflict-store.ts +12 -3
- package/src/consolidation-engine.ts +296 -8
- package/src/context-collapse-renderer.ts +222 -0
- package/src/context-composer-stable-selection.ts +5 -1
- package/src/context-composer.ts +239 -4
- package/src/context-reconstructor.ts +404 -0
- package/src/conversation-importer.ts +441 -0
- package/src/data-checkup.ts +232 -0
- package/src/decay-engine.ts +84 -0
- package/src/distill-lock.ts +122 -0
- package/src/doctor.ts +8 -10
- package/src/dream-pipeline.ts +227 -0
- package/src/emotion-detector.ts +90 -0
- package/src/eval.ts +3 -2
- package/src/forget-engine.ts +269 -0
- package/src/frequency-tracker.ts +175 -0
- package/src/graph-export.ts +644 -0
- package/src/ingest.ts +36 -6
- package/src/interference-detector.ts +187 -0
- package/src/kg-extractor.ts +15 -24
- package/src/kg-store.ts +2 -1
- package/src/language-hook.ts +67 -0
- package/src/llm-client.ts +289 -6
- package/src/mcp-server.ts +707 -80
- package/src/memory-assets.ts +22 -5
- package/src/memory-evolution.ts +365 -0
- package/src/memory-lint.ts +429 -0
- package/src/memory-output.ts +69 -0
- package/src/memory-schema.ts +74 -2
- package/src/narrative-schema.ts +79 -0
- package/src/narrative-tagger.ts +164 -0
- package/src/pii-detector.ts +98 -0
- package/src/post-retrieval-filter.ts +76 -0
- package/src/prediction-engine.ts +253 -0
- package/src/preference-slots.ts +65 -2
- package/src/prospective-memory.ts +215 -0
- package/src/query-expander.ts +67 -0
- package/src/recall-governor.ts +183 -0
- package/src/retention-policy.ts +90 -0
- package/src/retriever.ts +828 -55
- package/src/rif.ts +34 -8
- package/src/runtime-config.ts +23 -8
- package/src/scope-inventory.ts +4 -3
- package/src/scope-policy.ts +4 -0
- package/src/session-distiller.ts +307 -0
- package/src/session-output.ts +40 -0
- package/src/session-schema.ts +37 -0
- package/src/session-store.ts +7 -2
- package/src/skill-engine.ts +208 -0
- package/src/skill-promotion.ts +293 -0
- package/src/skill-schema.ts +39 -0
- package/src/store.ts +96 -10
- package/src/topic-tag.ts +66 -0
- package/src/tracker.ts +3 -1
- package/src/ui-server.ts +80 -8
- package/src/workflow-observation-scope-review.ts +2 -1
- package/src/workflow-observation-store.ts +2 -1
- package/src/write-verifier.ts +126 -0
package/src/auto-gc.ts
CHANGED
|
@@ -5,10 +5,20 @@
|
|
|
5
5
|
* AND enough time has passed since last GC. Inspired by NREM triple-coupling:
|
|
6
6
|
* multiple conditions must be satisfied before consolidation fires.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* B-2 upgrade: Uses evolution system's computeDecayScore + buildArchivedMetadata
|
|
9
|
+
* instead of raw age/importance heuristics. Archive-first, delete-never.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { MemoryStore } from "./store.js";
|
|
13
|
+
import {
|
|
14
|
+
parseEvolution,
|
|
15
|
+
computeDecayScore,
|
|
16
|
+
buildArchivedMetadata,
|
|
17
|
+
isActiveMemory,
|
|
18
|
+
} from "./memory-evolution.js";
|
|
19
|
+
import { loadRetentionPolicy, shouldArchiveByPolicy } from "./retention-policy.js";
|
|
20
|
+
import type { RetentionPolicy } from "./retention-policy.js";
|
|
21
|
+
import type { AuditLogger } from "./audit-log.js";
|
|
12
22
|
|
|
13
23
|
// ---------------------------------------------------------------------------
|
|
14
24
|
// Config
|
|
@@ -19,17 +29,20 @@ export interface AutoGcConfig {
|
|
|
19
29
|
minMemoryCount: number;
|
|
20
30
|
/** Minimum hours since last GC (default: 24) */
|
|
21
31
|
minHoursSinceLastGc: number;
|
|
22
|
-
/**
|
|
23
|
-
|
|
32
|
+
/** Decay score below which memories are archive candidates (default: 0.15) */
|
|
33
|
+
decayScoreThreshold: number;
|
|
24
34
|
/** Max entries to archive per run (default: 100) */
|
|
25
35
|
maxArchivePerRun: number;
|
|
36
|
+
/** Minimum age in days before a memory can be archived (default: 30) */
|
|
37
|
+
minAgeDays: number;
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
export const DEFAULT_AUTO_GC_CONFIG: AutoGcConfig = {
|
|
29
41
|
minMemoryCount: 1000,
|
|
30
42
|
minHoursSinceLastGc: 24,
|
|
31
|
-
|
|
43
|
+
decayScoreThreshold: 0.15,
|
|
32
44
|
maxArchivePerRun: 100,
|
|
45
|
+
minAgeDays: 30,
|
|
33
46
|
};
|
|
34
47
|
|
|
35
48
|
// ---------------------------------------------------------------------------
|
|
@@ -52,10 +65,18 @@ export interface GcResult {
|
|
|
52
65
|
/**
|
|
53
66
|
* Check conditions and run GC if all thresholds are met.
|
|
54
67
|
* Returns immediately if conditions not met (no-op).
|
|
68
|
+
*
|
|
69
|
+
* Archive criteria (B-2 evolution-aware):
|
|
70
|
+
* - Memory must be "active" (not already superseded/archived/consolidated)
|
|
71
|
+
* - Composite decay_score < threshold (default 0.15)
|
|
72
|
+
* - Age > minAgeDays (default 30 days)
|
|
73
|
+
* - Not pinned (importance >= 0.95 is considered pinned)
|
|
55
74
|
*/
|
|
56
75
|
export async function maybeRunGc(
|
|
57
76
|
store: MemoryStore,
|
|
58
77
|
config: AutoGcConfig = DEFAULT_AUTO_GC_CONFIG,
|
|
78
|
+
retentionConfigDir?: string,
|
|
79
|
+
auditLogger?: AuditLogger,
|
|
59
80
|
): Promise<GcResult> {
|
|
60
81
|
const stats = await store.stats();
|
|
61
82
|
const totalMemories = stats.total ?? 0;
|
|
@@ -74,8 +95,7 @@ export async function maybeRunGc(
|
|
|
74
95
|
// All conditions met — run GC
|
|
75
96
|
lastGcTimestamp = Date.now();
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
const cutoffMs = Date.now() - 60 * 86_400_000; // 60 days ago
|
|
98
|
+
const now = Date.now();
|
|
79
99
|
let archivedCount = 0;
|
|
80
100
|
let totalChecked = 0;
|
|
81
101
|
|
|
@@ -83,33 +103,82 @@ export async function maybeRunGc(
|
|
|
83
103
|
const entries = await store.list({ limit: 5000 });
|
|
84
104
|
totalChecked = entries.length;
|
|
85
105
|
|
|
106
|
+
// Pre-compute per-scope active memory counts and cache retention policies.
|
|
107
|
+
// This avoids repeated file reads inside the loop.
|
|
108
|
+
const activeCounts = new Map<string, number>();
|
|
109
|
+
const policyCache = new Map<string, RetentionPolicy>();
|
|
110
|
+
const scopesSeen = new Set<string>();
|
|
111
|
+
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const scope = entry.scope ?? "";
|
|
114
|
+
scopesSeen.add(scope);
|
|
115
|
+
if (isActiveMemory(entry.metadata)) {
|
|
116
|
+
activeCounts.set(scope, (activeCounts.get(scope) ?? 0) + 1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Batch-load retention policies for all scopes (one file read per scope)
|
|
121
|
+
for (const scope of scopesSeen) {
|
|
122
|
+
policyCache.set(scope, loadRetentionPolicy(scope, retentionConfigDir));
|
|
123
|
+
}
|
|
124
|
+
|
|
86
125
|
for (const entry of entries) {
|
|
87
126
|
if (archivedCount >= config.maxArchivePerRun) break;
|
|
88
127
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
128
|
+
// Only archive active memories
|
|
129
|
+
if (!isActiveMemory(entry.metadata)) continue;
|
|
130
|
+
|
|
131
|
+
// Never archive pinned memories (importance >= 0.95)
|
|
132
|
+
const importance = entry.importance ?? 0.5;
|
|
133
|
+
if (importance >= 0.95) continue;
|
|
134
|
+
|
|
135
|
+
// Check minimum age
|
|
136
|
+
const ageDays = (now - entry.timestamp) / 86_400_000;
|
|
137
|
+
|
|
138
|
+
// Decay-based archival (existing logic)
|
|
139
|
+
let shouldArchive = false;
|
|
140
|
+
if (ageDays >= config.minAgeDays) {
|
|
141
|
+
const evo = parseEvolution(entry.metadata, entry.timestamp);
|
|
142
|
+
let decayScore = computeDecayScore(evo, importance, now, entry.metadata);
|
|
143
|
+
// F3: Expired memories (validUntil < now) get 2x decay acceleration
|
|
144
|
+
if (evo.validUntil != null && evo.validUntil < now) {
|
|
145
|
+
decayScore *= 0.5; // Halve the score → archives faster
|
|
146
|
+
}
|
|
147
|
+
if (decayScore < config.decayScoreThreshold) {
|
|
148
|
+
shouldArchive = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Retention-policy-based archival (F-2): OR with decay-based
|
|
153
|
+
if (!shouldArchive) {
|
|
154
|
+
const scope = entry.scope ?? "";
|
|
155
|
+
const policy = policyCache.get(scope) ?? loadRetentionPolicy(scope, retentionConfigDir);
|
|
156
|
+
const activeCount = activeCounts.get(scope) ?? 0;
|
|
157
|
+
const policyCheck = shouldArchiveByPolicy(policy, ageDays, activeCount);
|
|
158
|
+
if (policyCheck.archive) {
|
|
159
|
+
shouldArchive = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (shouldArchive) {
|
|
164
|
+
const archivedMeta = buildArchivedMetadata(entry.metadata);
|
|
165
|
+
await store.update(entry.id, { metadata: archivedMeta });
|
|
112
166
|
archivedCount++;
|
|
167
|
+
// F-1: Audit log — record archive operation (silent on failure)
|
|
168
|
+
try {
|
|
169
|
+
auditLogger?.log({
|
|
170
|
+
operation: "archive",
|
|
171
|
+
memoryId: entry.id,
|
|
172
|
+
actor: "system",
|
|
173
|
+
details: `auto-gc: age=${Math.floor(ageDays)}d`,
|
|
174
|
+
});
|
|
175
|
+
} catch { /* Audit must never block GC */ }
|
|
176
|
+
// Decrement active count for the scope (memory is no longer active)
|
|
177
|
+
const scope = entry.scope ?? "";
|
|
178
|
+
const prev = activeCounts.get(scope) ?? 0;
|
|
179
|
+
if (prev > 0) {
|
|
180
|
+
activeCounts.set(scope, prev - 1);
|
|
181
|
+
}
|
|
113
182
|
}
|
|
114
183
|
}
|
|
115
184
|
|
package/src/auto-recall.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { composeResumeContext, type ResumeContextDeps } from "./context-composer.js";
|
|
2
|
+
import { resolveTier } from "./decay-engine.js";
|
|
2
3
|
import type { DurableMemoryCategory } from "./memory-schema.js";
|
|
4
|
+
import { governResults, truncateQuery, type GovernorConfig, type GovernorSession } from "./recall-governor.js";
|
|
3
5
|
import type { RetrievalResult } from "./retriever.js";
|
|
4
6
|
import { buildRetrievalContext, resolveScopeSelection } from "./scope-policy.js";
|
|
5
7
|
import type { RetrievalProfileName, ResumeContextResponse } from "./session-schema.js";
|
|
@@ -17,6 +19,10 @@ export interface AutoRecallRequest {
|
|
|
17
19
|
profile?: RetrievalProfileName;
|
|
18
20
|
env?: NodeJS.ProcessEnv;
|
|
19
21
|
operation?: string;
|
|
22
|
+
/** Governor config overrides (charBudget, maxItems, maxQueryChars) */
|
|
23
|
+
governor?: Partial<GovernorConfig>;
|
|
24
|
+
/** Governor session for cross-call dedup within a single conversation turn */
|
|
25
|
+
governorSession?: GovernorSession;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
export interface AutoRecallResponse {
|
|
@@ -31,8 +37,10 @@ export async function runAutoRecall(
|
|
|
31
37
|
deps: ResumeContextDeps,
|
|
32
38
|
request: AutoRecallRequest,
|
|
33
39
|
): Promise<AutoRecallResponse> {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
40
|
+
const govCfg = request.governor;
|
|
41
|
+
const maxQ = govCfg?.maxQueryChars ?? 1000;
|
|
42
|
+
const message = truncateQuery(request.message.replace(/\s+/g, " ").trim(), maxQ);
|
|
43
|
+
const task = truncateQuery((request.task || message).replace(/\s+/g, " ").trim(), maxQ);
|
|
36
44
|
|
|
37
45
|
if (!message) {
|
|
38
46
|
throw new Error("message is required");
|
|
@@ -72,9 +80,11 @@ export async function runAutoRecall(
|
|
|
72
80
|
};
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
const
|
|
83
|
+
const effectiveLimit = request.limit || 5;
|
|
84
|
+
// Fetch extra candidates so tier filtering still yields enough results
|
|
85
|
+
const rawResults = await deps.retriever.retrieve(buildRetrievalContext({
|
|
76
86
|
query: message,
|
|
77
|
-
limit:
|
|
87
|
+
limit: effectiveLimit * 3,
|
|
78
88
|
category: request.category,
|
|
79
89
|
scope: resolvedScope,
|
|
80
90
|
sessionId: request.sessionId,
|
|
@@ -86,6 +96,25 @@ export async function runAutoRecall(
|
|
|
86
96
|
allowUnscoped: request.allScopes === true,
|
|
87
97
|
}));
|
|
88
98
|
|
|
99
|
+
// Tier-aware filtering: prefer core/working memories, backfill with peripheral.
|
|
100
|
+
// Peripheral memories get a 15% score discount to reduce noise injection.
|
|
101
|
+
const promoted: RetrievalResult[] = [];
|
|
102
|
+
const demoted: RetrievalResult[] = [];
|
|
103
|
+
for (const r of rawResults) {
|
|
104
|
+
const tier = resolveTier(r.entry.metadata);
|
|
105
|
+
if (tier === "core" || tier === "working") {
|
|
106
|
+
promoted.push(r);
|
|
107
|
+
} else {
|
|
108
|
+
demoted.push({ ...r, score: r.score * 0.85 });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const tierFiltered = [...promoted, ...demoted]
|
|
112
|
+
.sort((a, b) => b.score - a.score)
|
|
113
|
+
.slice(0, effectiveLimit);
|
|
114
|
+
|
|
115
|
+
// LME-7: Recall Governor — evolution filter, budget control, session dedup
|
|
116
|
+
const results = governResults(tierFiltered, request.governorSession, govCfg);
|
|
117
|
+
|
|
89
118
|
return {
|
|
90
119
|
mode: "resume+search",
|
|
91
120
|
resolvedScope,
|