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.
- package/CHANGELOG.md +37 -0
- package/README.md +34 -9
- package/README_CN.md +37 -12
- package/package.json +4 -4
- package/src/__tests__/access-tracker-novelty.test.ts +66 -0
- package/src/__tests__/api-server-security.test.ts +102 -0
- package/src/__tests__/belief-history.test.ts +220 -0
- package/src/__tests__/capture-engine.test.ts +93 -3
- package/src/__tests__/category-min-scores.test.ts +83 -0
- package/src/__tests__/cluster-consolidation.test.ts +167 -4
- package/src/__tests__/conflict-store.test.ts +46 -2
- package/src/__tests__/consolidation-engine.test.ts +97 -0
- package/src/__tests__/contracts.test.ts +46 -0
- package/src/__tests__/decay-exemption.test.ts +45 -0
- package/src/__tests__/dream-pipeline.test.ts +464 -5
- package/src/__tests__/freshness.test.ts +186 -0
- package/src/__tests__/hotness-blend.test.ts +207 -0
- package/src/__tests__/llm-client-pivot.test.ts +628 -0
- package/src/__tests__/maintenance-locks.test.ts +15 -0
- package/src/__tests__/memory-boundaries.test.ts +32 -0
- package/src/__tests__/noise-prototype-bank.test.ts +280 -0
- package/src/__tests__/pii-redact.test.ts +19 -0
- package/src/__tests__/pivot-apply.test.ts +373 -0
- package/src/__tests__/pivot-distill-supervisor.test.ts +457 -0
- package/src/__tests__/pivot-distill.test.ts +1178 -0
- package/src/__tests__/preference-matcher.test.ts +54 -2
- package/src/__tests__/pull-from-macbook-sync-only.test.ts +679 -0
- package/src/__tests__/recall-gate.test.ts +266 -0
- package/src/__tests__/resolve-db-path.test.ts +130 -0
- package/src/__tests__/resolve-tier.test.ts +64 -0
- package/src/__tests__/retrieval-eval.test.ts +163 -0
- package/src/__tests__/retriever-lazy-init-dispatch.test.ts +123 -0
- package/src/__tests__/skill-promotion.test.ts +53 -0
- package/src/__tests__/stable-context-length-contract.test.ts +62 -0
- package/src/__tests__/store-read-consistency.test.ts +122 -0
- package/src/__tests__/store-schema-migration.test.ts +87 -0
- package/src/__tests__/store-upsert.test.ts +17 -0
- package/src/__tests__/term-registry-terminal-roster.test.ts +127 -0
- package/src/__tests__/ui-server-security.test.ts +93 -0
- package/src/access-tracker.ts +53 -3
- package/src/adaptive-retrieval.ts +11 -0
- package/src/api-server.ts +9 -2
- package/src/auto-recall.ts +61 -1
- package/src/belief-history.ts +164 -0
- package/src/capture-engine.ts +88 -11
- package/src/cli.ts +109 -14
- package/src/conflict-engine.ts +24 -3
- package/src/consolidation-engine.ts +146 -12
- package/src/context-composer-stable-selection.ts +7 -1
- package/src/data-checkup.ts +1 -1
- package/src/decay-engine.ts +59 -7
- package/src/doctor.ts +4 -2
- package/src/dream-pipeline.ts +359 -11
- package/src/env-config.ts +41 -0
- package/src/eval.ts +154 -11
- package/src/freshness.ts +192 -0
- package/src/http-request-policy.ts +148 -0
- package/src/ingest.ts +277 -2
- package/src/kg-extractor.ts +7 -1
- package/src/llm-client.ts +106 -11
- package/src/mcp-server.ts +2 -1
- package/src/mcp-tools-advanced.ts +13 -0
- package/src/mcp-tools-core.ts +5 -1
- package/src/memory-boundaries.ts +23 -2
- package/src/memory-health-rebalance.ts +4 -0
- package/src/memory-output.ts +16 -2
- package/src/memory-store-port.ts +3 -0
- package/src/noise-prototype-bank.ts +408 -0
- package/src/pii-detector.ts +52 -2
- package/src/pivot-apply.ts +399 -0
- package/src/preference-matcher.ts +37 -3
- package/src/recall-gate.ts +305 -0
- package/src/retrieval-profiles.ts +46 -4
- package/src/retriever.ts +95 -18
- package/src/retroactive-boost.ts +6 -2
- package/src/runtime-config.ts +70 -5
- package/src/skill-promotion.ts +58 -2
- package/src/store.ts +54 -19
- package/src/term-registry.ts +35 -0
- package/src/ui-server.ts +7 -0
- package/src/version.ts +19 -0
|
@@ -162,7 +162,13 @@ function formatStableResult(category: StableCategory, result: RetrievalResult):
|
|
|
162
162
|
// F1: Tag low-confidence memories in resume_context output
|
|
163
163
|
const conf = getConfidence(result.entry);
|
|
164
164
|
const tag = conf < 0.5 ? " [低置信]" : "";
|
|
165
|
-
|
|
165
|
+
// 220 是 stableContext 的 schema 上限(session-schema.ts:61),不是随手写的数:
|
|
166
|
+
// 这里曾是 230,比上限大 10,而 interleaveUnique(context-composer-stable.ts:199-206)
|
|
167
|
+
// 不再截一次 —— 于是任何 221~230 字符的行都会让 composeResumeContext 末尾的
|
|
168
|
+
// ResumeContextResponseSchema.parse(context-composer.ts:541)抛
|
|
169
|
+
// "stableContext must be at most 220 characters",整个 resume_context 调用失败。
|
|
170
|
+
// 改这个数之前先看 schema,两边必须一起动。
|
|
171
|
+
return cleanText(`${STABLE_CATEGORY_LABELS[category]}: ${text}${tag}`, 220);
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
export function selectStableResults(
|
package/src/data-checkup.ts
CHANGED
|
@@ -98,7 +98,7 @@ function checkOrphanMemories(entries: MemoryEntry[]): CheckResult {
|
|
|
98
98
|
function checkTierDistribution(entries: MemoryEntry[]): CheckResult {
|
|
99
99
|
const tierCounts: Record<MemoryTier, number> = { core: 0, working: 0, peripheral: 0 };
|
|
100
100
|
for (const e of entries) {
|
|
101
|
-
const tier = resolveTier(e.metadata);
|
|
101
|
+
const tier = resolveTier(e.metadata, e.importance);
|
|
102
102
|
tierCounts[tier]++;
|
|
103
103
|
}
|
|
104
104
|
|
package/src/decay-engine.ts
CHANGED
|
@@ -122,26 +122,63 @@ export function computeArousalBoost(
|
|
|
122
122
|
// HP-7: Decay Exemptions
|
|
123
123
|
// ============================================================================
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Categories whose entries are procedural memory (knowing *how*), not declarative.
|
|
127
|
+
*
|
|
128
|
+
* Only `patterns` qualifies: RecallNest's skill schema stores skills under
|
|
129
|
+
* `category = "patterns"` (see memory-schema.ts), so this covers both workflow
|
|
130
|
+
* patterns and skills.
|
|
131
|
+
*
|
|
132
|
+
* `cases` is deliberately NOT here. A case is a problem/solution pair with a real
|
|
133
|
+
* episodic component ("that CI failure on 07-24"), and its solution can go stale
|
|
134
|
+
* when the underlying tool changes — it *should* decay. Procedural rules do not:
|
|
135
|
+
* see the "什么该衰减" discussion in hippo [[条件失效优于时间衰减]].
|
|
136
|
+
*/
|
|
137
|
+
export const PROCEDURAL_CATEGORIES = new Set<string>(["patterns"]);
|
|
138
|
+
|
|
125
139
|
/**
|
|
126
140
|
* Check if a memory should be exempt from time decay.
|
|
127
141
|
* Exempt entries keep their original score without time-based penalties.
|
|
128
142
|
*
|
|
129
143
|
* Exemption rules:
|
|
144
|
+
* 0. Procedural category → knowing-how doesn't fade with wall-clock time
|
|
130
145
|
* 1. Core tier + high importance (≥ 0.95) → identity/correction, must not fade
|
|
131
146
|
* 2. Recently accessed (within 7 days) → actively used, not stale
|
|
132
147
|
* 3. Pinned → user explicitly marked as persistent
|
|
148
|
+
*
|
|
149
|
+
* @param category Optional entry category. Omitting it preserves the pre-Rule-0
|
|
150
|
+
* behaviour exactly, so existing callers stay valid.
|
|
133
151
|
*/
|
|
134
152
|
export function isDecayExempt(
|
|
135
153
|
metadata: string | undefined,
|
|
136
154
|
importance: number,
|
|
155
|
+
category?: string,
|
|
137
156
|
): boolean {
|
|
157
|
+
// Rule 0: Procedural memory is exempt from *time* decay.
|
|
158
|
+
//
|
|
159
|
+
// Rules 1-3 all key off access statistics or explicit user action, which
|
|
160
|
+
// systematically punishes procedural memory: its defining trait is low access
|
|
161
|
+
// frequency + long validity ("restart launchd with bootout+sleep+bootstrap" may
|
|
162
|
+
// be needed once a quarter but is correct every time). Combined with `patterns`
|
|
163
|
+
// carrying the highest retrieval threshold in DEFAULT_CATEGORY_MIN_SCORES (0.45),
|
|
164
|
+
// that produced a feedback trap: ranked lower → read less → ranked lower still.
|
|
165
|
+
//
|
|
166
|
+
// Cognitive-science backing: procedural memory is non-declarative (Squire) and is
|
|
167
|
+
// the most decay-resistant human memory type — a different curve, not a slower one.
|
|
168
|
+
//
|
|
169
|
+
// NOTE: this exempts from *time* decay only. Procedural memory still expires, but
|
|
170
|
+
// by condition (its premise changed), not by age. Deliberately checked before the
|
|
171
|
+
// `!metadata` guard so entries without metadata are still covered.
|
|
172
|
+
if (category !== undefined && PROCEDURAL_CATEGORIES.has(category)) return true;
|
|
173
|
+
|
|
138
174
|
if (!metadata) return false;
|
|
139
175
|
|
|
140
176
|
try {
|
|
141
177
|
const meta = JSON.parse(metadata) as Record<string, unknown>;
|
|
142
178
|
|
|
143
179
|
// Rule 1: Core + very high importance
|
|
144
|
-
|
|
180
|
+
// importance 是本函数的入参(来自 entry 列),一并传下去——否则 heuristic 读不到它。
|
|
181
|
+
const tier = meta.tier ?? resolveTierFromMeta(meta, importance);
|
|
145
182
|
if (tier === "core" && importance >= 0.95) return true;
|
|
146
183
|
|
|
147
184
|
// Rule 2: Accessed within last 7 days
|
|
@@ -158,12 +195,21 @@ export function isDecayExempt(
|
|
|
158
195
|
}
|
|
159
196
|
}
|
|
160
197
|
|
|
161
|
-
/**
|
|
162
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Internal helper: resolve tier from parsed metadata (avoids re-parsing).
|
|
200
|
+
*
|
|
201
|
+
* `entryImportance` 是 MemoryEntry 的列字段。必须由调用方传进来:importance 不存在
|
|
202
|
+
* 于 metadata(accessCount / lastAccessedAt / tier 才是 access-tracker 写在顶层的),
|
|
203
|
+
* 所以只读 meta.importance 会恒等于 0,让所有还没有显式 tier 的条目一律落到
|
|
204
|
+
* peripheral——2026-07 实测全库 3214 条走这个分支,顶层有 importance 的是 0 条。
|
|
205
|
+
*/
|
|
206
|
+
function resolveTierFromMeta(meta: Record<string, unknown>, entryImportance?: number): MemoryTier {
|
|
163
207
|
if (meta.tier === "core" || meta.tier === "working" || meta.tier === "peripheral") {
|
|
164
208
|
return meta.tier;
|
|
165
209
|
}
|
|
166
|
-
const imp = typeof meta.importance === "number"
|
|
210
|
+
const imp = typeof meta.importance === "number"
|
|
211
|
+
? meta.importance
|
|
212
|
+
: (typeof entryImportance === "number" ? entryImportance : 0);
|
|
167
213
|
const ac = typeof meta.accessCount === "number" ? meta.accessCount : 0;
|
|
168
214
|
if (imp >= 0.95 || ac >= 10) return "core";
|
|
169
215
|
if (imp >= 0.8 || ac >= 3) return "working";
|
|
@@ -178,13 +224,13 @@ function resolveTierFromMeta(meta: Record<string, unknown>): MemoryTier {
|
|
|
178
224
|
* Determine a memory's current tier from its metadata.
|
|
179
225
|
* Falls back to heuristic based on importance if no tier is stored.
|
|
180
226
|
*/
|
|
181
|
-
export function resolveTier(metadata?: string): MemoryTier {
|
|
227
|
+
export function resolveTier(metadata?: string, entryImportance?: number): MemoryTier {
|
|
182
228
|
if (!metadata) return "peripheral";
|
|
183
229
|
|
|
184
230
|
try {
|
|
185
231
|
const meta = JSON.parse(metadata) as Record<string, unknown>;
|
|
186
232
|
|
|
187
|
-
// Explicit tier stored in metadata
|
|
233
|
+
// Explicit tier stored in metadata(access-tracker 算过一次就会写在这里)
|
|
188
234
|
if (meta.tier === "core" || meta.tier === "working" || meta.tier === "peripheral") {
|
|
189
235
|
return meta.tier;
|
|
190
236
|
}
|
|
@@ -193,7 +239,13 @@ export function resolveTier(metadata?: string): MemoryTier {
|
|
|
193
239
|
// - Pinned assets (importance ≥ 0.95) → core
|
|
194
240
|
// - High importance (≥ 0.8) → working
|
|
195
241
|
// - Everything else → peripheral
|
|
196
|
-
|
|
242
|
+
//
|
|
243
|
+
// importance 优先取调用方传入的 entry 列值。metadata 里没有这个字段——顶层住着的是
|
|
244
|
+
// accessCount / lastAccessedAt / tier(access-tracker 写的),importance 从来不在。
|
|
245
|
+
// 不传就只能退回 0,那会把所有还没被访问过的条目一律压成 peripheral。
|
|
246
|
+
const importance = typeof meta.importance === "number"
|
|
247
|
+
? meta.importance
|
|
248
|
+
: (typeof entryImportance === "number" ? entryImportance : 0);
|
|
197
249
|
const accessCount = typeof meta.accessCount === "number" ? meta.accessCount : 0;
|
|
198
250
|
|
|
199
251
|
if (importance >= 0.95 || accessCount >= 10) return "core";
|
package/src/doctor.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { existsSync, accessSync, constants, readFileSync } from "node:fs";
|
|
|
14
14
|
import { resolve, join } from "node:path";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
16
|
import { metaDir } from "./compat.js";
|
|
17
|
-
import { loadConfig, expandHome, resolveEnv, findConfigPath, loadDotEnv } from "./runtime-config.js";
|
|
17
|
+
import { loadConfig, expandHome, resolveEnv, findConfigPath, loadDotEnv, resolveDbPath } from "./runtime-config.js";
|
|
18
18
|
import { createEmbedder, type EmbeddingConfig } from "./embedder.js";
|
|
19
19
|
import { extractCanonicalKey, normalizeCanonicalKey } from "./memory-boundaries.js";
|
|
20
20
|
import {
|
|
@@ -356,7 +356,9 @@ export async function runDoctor(options: { ci?: boolean } = {}): Promise<CheckRe
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
// 5. LanceDB data directory
|
|
359
|
-
|
|
359
|
+
// Must resolve exactly like the server does — a doctor that reports on a
|
|
360
|
+
// different directory than the one actually in use is worse than no doctor.
|
|
361
|
+
const dbPath = resolveDbPath(config);
|
|
360
362
|
try {
|
|
361
363
|
validateStoragePath(dbPath);
|
|
362
364
|
results.push(pass("Data directory", dbPath));
|