@hilbras/remembra 0.2.0 → 0.4.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 +41 -0
- package/README.md +9 -3
- package/dist/embeddings.d.ts +12 -0
- package/dist/embeddings.js +65 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/http.js +18 -0
- package/dist/http.js.map +1 -1
- package/dist/index.js +42 -2
- package/dist/index.js.map +1 -1
- package/dist/llm.d.ts +33 -0
- package/dist/llm.js +168 -0
- package/dist/llm.js.map +1 -0
- package/dist/retrieval.d.ts +11 -6
- package/dist/retrieval.js +57 -25
- package/dist/retrieval.js.map +1 -1
- package/dist/service.d.ts +76 -5
- package/dist/service.js +225 -5
- package/dist/service.js.map +1 -1
- package/dist/store.d.ts +18 -8
- package/dist/store.js +83 -17
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.js.map +1 -1
- package/docs/chatgpt.md +11 -0
- package/docs/clients.md +6 -0
- package/docs/lifecycle.md +94 -0
- package/docs/providers.md +107 -0
- package/docs/tools.md +27 -2
- package/package.json +1 -1
package/dist/retrieval.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import { cosine } from "./embeddings.js";
|
|
1
2
|
/**
|
|
2
|
-
* Layered retrieval (
|
|
3
|
-
* 1. Roles always pass (they are instructions).
|
|
4
|
-
* 2. Scope match (global + current scope beat other scopes).
|
|
5
|
-
* 3. Importance, recency, and keyword overlap contribute to the score.
|
|
3
|
+
* Layered retrieval (decisions Q4 + v2-Q3):
|
|
6
4
|
*
|
|
7
|
-
*
|
|
5
|
+
* Hard gates (never bypassed by scores):
|
|
6
|
+
* - roles always pass (+1000)
|
|
7
|
+
* - other scopes' memories are excluded entirely
|
|
8
|
+
*
|
|
9
|
+
* Ranking:
|
|
10
|
+
* - embeddings ON (queryVec provided): semantic similarity is the primary
|
|
11
|
+
* signal; importance + recency act as modifiers; keywords still help as
|
|
12
|
+
* a tie-breaker for memories missing vectors.
|
|
13
|
+
* - embeddings OFF: keyword overlap is the primary signal (v1 behavior).
|
|
8
14
|
*/
|
|
9
|
-
export function search(memories, q) {
|
|
15
|
+
export function search(memories, q, queryVec) {
|
|
10
16
|
const now = Date.now();
|
|
11
17
|
const terms = (q.query ?? "")
|
|
12
18
|
.toLowerCase()
|
|
@@ -14,38 +20,64 @@ export function search(memories, q) {
|
|
|
14
20
|
.filter((t) => t.length > 1);
|
|
15
21
|
const scored = memories
|
|
16
22
|
.filter((m) => (q.type ? m.type === q.type : true))
|
|
17
|
-
.map((m) => ({ m, score: score(m, terms, q.scope, now) }))
|
|
23
|
+
.map((m) => ({ m, score: score(m, terms, q.scope, now, queryVec) }))
|
|
18
24
|
.filter(({ m, score }) => m.type === "role" || score > 0)
|
|
19
25
|
.sort((a, b) => b.score - a.score || b.m.updatedAt.localeCompare(a.m.updatedAt));
|
|
20
26
|
return scored.slice(0, q.limit ?? 10).map(({ m }) => m);
|
|
21
27
|
}
|
|
22
|
-
function score(m, terms, scope, now) {
|
|
28
|
+
function score(m, terms, scope, now, queryVec) {
|
|
23
29
|
let s = 0;
|
|
30
|
+
// --- hard gates ---
|
|
24
31
|
if (m.type === "role")
|
|
25
32
|
s += 1000;
|
|
33
|
+
if (scope && m.scope !== scope && m.scope !== "global")
|
|
34
|
+
return 0; // no cross-project leaks
|
|
26
35
|
if (m.scope === "global")
|
|
27
36
|
s += 100;
|
|
28
37
|
if (scope && m.scope === scope)
|
|
29
38
|
s += 150;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
// --- semantic mode (embeddings on) ---
|
|
40
|
+
if (queryVec && queryVec.length > 0) {
|
|
41
|
+
if (m.embedding && m.embedding.length > 0) {
|
|
42
|
+
// Primary signal: cosine similarity (0..1 → up to +100).
|
|
43
|
+
const sim = Math.max(0, cosine(queryVec, m.embedding));
|
|
44
|
+
s += sim * 100;
|
|
45
|
+
// Similarity gate: near-zero matches only survive on role/scope strength.
|
|
46
|
+
if (sim < 0.05 && m.type !== "role" && m.scope !== "global" && scope !== m.scope)
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Memory without a vector: fall back to keyword scoring for it.
|
|
51
|
+
s += keywordScore(m, terms);
|
|
52
|
+
}
|
|
53
|
+
// Modifiers (smaller weight than keywords-only mode).
|
|
54
|
+
s += m.importance * 4;
|
|
55
|
+
s += recencyScore(m, now) * 0.5;
|
|
56
|
+
return s;
|
|
45
57
|
}
|
|
46
|
-
|
|
58
|
+
// --- keyword mode (embeddings off — v1 behavior) ---
|
|
59
|
+
s += m.importance * 10;
|
|
60
|
+
s += recencyScore(m, now);
|
|
61
|
+
if (terms.length > 0)
|
|
62
|
+
s += keywordScore(m, terms);
|
|
63
|
+
else
|
|
47
64
|
s += 10; // no query: everything eligible scores a little
|
|
48
|
-
}
|
|
49
65
|
return s;
|
|
50
66
|
}
|
|
67
|
+
function recencyScore(m, now) {
|
|
68
|
+
const ageDays = (now - Date.parse(m.updatedAt)) / 86_400_000;
|
|
69
|
+
if (Number.isNaN(ageDays))
|
|
70
|
+
return 0;
|
|
71
|
+
return Math.max(0, 20 - ageDays / 3);
|
|
72
|
+
}
|
|
73
|
+
function keywordScore(m, terms) {
|
|
74
|
+
if (terms.length === 0)
|
|
75
|
+
return 0;
|
|
76
|
+
const hay = (m.content + " " + m.tags.join(" ")).toLowerCase();
|
|
77
|
+
let hits = 0;
|
|
78
|
+
for (const t of terms)
|
|
79
|
+
if (hay.includes(t))
|
|
80
|
+
hits++;
|
|
81
|
+
return (hits / terms.length) * 60;
|
|
82
|
+
}
|
|
51
83
|
//# sourceMappingURL=retrieval.js.map
|
package/dist/retrieval.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../src/retrieval.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../src/retrieval.ts"],"names":[],"mappings":"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"}
|
package/dist/service.d.ts
CHANGED
|
@@ -1,16 +1,65 @@
|
|
|
1
1
|
import { MemoryStore } from "./store.js";
|
|
2
|
-
import { MemoryType } from "./types.js";
|
|
2
|
+
import { MemoryType, Memory } from "./types.js";
|
|
3
|
+
import { EmbeddingProvider } from "./embeddings.js";
|
|
4
|
+
import { LlmProvider, ExtractedMemory } from "./llm.js";
|
|
5
|
+
export interface DigestResult {
|
|
6
|
+
extracted: number;
|
|
7
|
+
stored: Memory[];
|
|
8
|
+
skippedDuplicates: number;
|
|
9
|
+
merged: number;
|
|
10
|
+
ids: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface MaintainResult {
|
|
13
|
+
archived: string[];
|
|
14
|
+
deleted: string[];
|
|
15
|
+
embedded: number;
|
|
16
|
+
revived?: number;
|
|
17
|
+
}
|
|
18
|
+
interface ServiceDeps {
|
|
19
|
+
embeddingProvider?: EmbeddingProvider;
|
|
20
|
+
llmProvider?: LlmProvider;
|
|
21
|
+
/** Injection points for tests. */
|
|
22
|
+
embedFn?: (text: string) => Promise<number[]>;
|
|
23
|
+
extractFn?: (transcript: string) => Promise<ExtractedMemory[]>;
|
|
24
|
+
mergeFn?: (newContent: string, existing: {
|
|
25
|
+
type: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}) => Promise<{
|
|
28
|
+
action: "store";
|
|
29
|
+
} | {
|
|
30
|
+
action: "skip";
|
|
31
|
+
} | {
|
|
32
|
+
action: "merge";
|
|
33
|
+
content: string;
|
|
34
|
+
}>;
|
|
35
|
+
/** How often the opportunistic decay pass may run on search (ms). Default 1h. */
|
|
36
|
+
decayIntervalMs?: number;
|
|
37
|
+
/** Active memory unused for this many days gets archived. Default 90. */
|
|
38
|
+
archiveAfterDays?: number;
|
|
39
|
+
/** Archived memory older than this gets auto-deleted. Default 365. */
|
|
40
|
+
archiveTtlDays?: number;
|
|
41
|
+
}
|
|
3
42
|
/**
|
|
4
43
|
* Transport-agnostic handlers. Both the MCP tools and the HTTP API
|
|
5
44
|
* call into this module, so behavior is guaranteed to match.
|
|
6
45
|
*/
|
|
7
46
|
export declare class MemoryService {
|
|
8
47
|
readonly db: MemoryStore;
|
|
9
|
-
|
|
48
|
+
private readonly embedFn?;
|
|
49
|
+
private readonly extractFn;
|
|
50
|
+
private readonly mergeFn;
|
|
51
|
+
private readonly decayIntervalMs;
|
|
52
|
+
private readonly archiveAfterDays;
|
|
53
|
+
private readonly archiveTtlDays;
|
|
54
|
+
private lastDecayRun;
|
|
55
|
+
private decayRunning;
|
|
56
|
+
constructor(db: MemoryStore, deps?: ServiceDeps);
|
|
57
|
+
get embeddingsEnabled(): boolean;
|
|
58
|
+
private maybeEmbed;
|
|
10
59
|
store(input: unknown): Promise<{
|
|
11
60
|
id: string;
|
|
12
61
|
message: string;
|
|
13
|
-
memory:
|
|
62
|
+
memory: Memory;
|
|
14
63
|
}>;
|
|
15
64
|
search(q: {
|
|
16
65
|
query?: string;
|
|
@@ -19,17 +68,39 @@ export declare class MemoryService {
|
|
|
19
68
|
limit?: number;
|
|
20
69
|
}): Promise<{
|
|
21
70
|
text: string;
|
|
22
|
-
results:
|
|
71
|
+
results: Memory[];
|
|
23
72
|
}>;
|
|
24
73
|
list(q: {
|
|
25
74
|
scope?: string;
|
|
26
75
|
type?: MemoryType;
|
|
76
|
+
includeArchived?: boolean;
|
|
27
77
|
}): Promise<{
|
|
28
78
|
text: string;
|
|
29
|
-
memories:
|
|
79
|
+
memories: Memory[];
|
|
30
80
|
}>;
|
|
31
81
|
forget(id: string): Promise<{
|
|
32
82
|
ok: boolean;
|
|
33
83
|
text: string;
|
|
34
84
|
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Session digest (v2) with contradiction-merge (v3):
|
|
87
|
+
* extract worth-keeping memories, skip exact duplicates, and let the LLM
|
|
88
|
+
* merge items that evolved from a stored memory (superseded text preserved).
|
|
89
|
+
*/
|
|
90
|
+
digest(opts: {
|
|
91
|
+
transcript: string;
|
|
92
|
+
scope?: string;
|
|
93
|
+
source?: string;
|
|
94
|
+
}): Promise<DigestResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Explicit maintenance (decision v3-Q1): decay sweep + vector backfill.
|
|
97
|
+
* Exposed as the `memory_maintain` tool, POST /maintain, and the CLI.
|
|
98
|
+
*/
|
|
99
|
+
maintain(): Promise<MaintainResult>;
|
|
100
|
+
/** Decay lifecycle: unused actives → archived → auto-deleted past TTL. */
|
|
101
|
+
private decayPass;
|
|
102
|
+
private maybeRunDecay;
|
|
103
|
+
/** Pick the most similar stored memory of the same type+scope for merge comparison. */
|
|
104
|
+
private findCandidate;
|
|
35
105
|
}
|
|
106
|
+
export {};
|
package/dist/service.js
CHANGED
|
@@ -1,16 +1,52 @@
|
|
|
1
1
|
import { search } from "./retrieval.js";
|
|
2
2
|
import { StoreInput } from "./types.js";
|
|
3
|
+
import { resolveEmbeddingProvider, embedText, cosine } from "./embeddings.js";
|
|
4
|
+
import { resolveLlmProvider, extractMemories, resolveMerge, } from "./llm.js";
|
|
3
5
|
/**
|
|
4
6
|
* Transport-agnostic handlers. Both the MCP tools and the HTTP API
|
|
5
7
|
* call into this module, so behavior is guaranteed to match.
|
|
6
8
|
*/
|
|
7
9
|
export class MemoryService {
|
|
8
10
|
db;
|
|
9
|
-
|
|
11
|
+
embedFn;
|
|
12
|
+
extractFn;
|
|
13
|
+
mergeFn;
|
|
14
|
+
decayIntervalMs;
|
|
15
|
+
archiveAfterDays;
|
|
16
|
+
archiveTtlDays;
|
|
17
|
+
lastDecayRun = 0;
|
|
18
|
+
decayRunning = false;
|
|
19
|
+
constructor(db, deps = {}) {
|
|
10
20
|
this.db = db;
|
|
21
|
+
const emb = deps.embeddingProvider ?? resolveEmbeddingProvider();
|
|
22
|
+
const llm = deps.llmProvider ?? resolveLlmProvider();
|
|
23
|
+
this.embedFn =
|
|
24
|
+
deps.embedFn ??
|
|
25
|
+
(emb === "none" ? undefined : async (text) => embedText(text, emb));
|
|
26
|
+
this.extractFn = deps.extractFn ?? (async (t) => extractMemories(t, llm));
|
|
27
|
+
this.mergeFn = deps.mergeFn ?? ((n, e) => resolveMerge(n, e, llm));
|
|
28
|
+
this.decayIntervalMs = deps.decayIntervalMs ?? 3_600_000; // 1h
|
|
29
|
+
this.archiveAfterDays = deps.archiveAfterDays ?? Number(process.env.REMEMBRA_ARCHIVE_AFTER_DAYS ?? 90);
|
|
30
|
+
this.archiveTtlDays = deps.archiveTtlDays ?? Number(process.env.REMEMBRA_ARCHIVE_TTL_DAYS ?? 365);
|
|
31
|
+
}
|
|
32
|
+
get embeddingsEnabled() {
|
|
33
|
+
return this.embedFn !== undefined;
|
|
34
|
+
}
|
|
35
|
+
async maybeEmbed(text) {
|
|
36
|
+
if (!this.embedFn)
|
|
37
|
+
return undefined;
|
|
38
|
+
try {
|
|
39
|
+
return await this.embedFn(text);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(`Remembra: embedding failed (${err instanceof Error ? err.message : err}); continuing without`);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
11
45
|
}
|
|
12
46
|
async store(input) {
|
|
13
|
-
const
|
|
47
|
+
const parsed = StoreInput.parse(input);
|
|
48
|
+
const embedding = await this.maybeEmbed(parsed.content);
|
|
49
|
+
const memory = await this.db.store(parsed, embedding);
|
|
14
50
|
return {
|
|
15
51
|
id: memory.id,
|
|
16
52
|
message: `Stored ${memory.type} memory ${memory.id} (scope: ${memory.scope})`,
|
|
@@ -18,7 +54,15 @@ export class MemoryService {
|
|
|
18
54
|
};
|
|
19
55
|
}
|
|
20
56
|
async search(q) {
|
|
21
|
-
|
|
57
|
+
let queryVec = null;
|
|
58
|
+
if (q.query && this.embedFn)
|
|
59
|
+
queryVec = (await this.maybeEmbed(q.query)) ?? null;
|
|
60
|
+
const results = search(await this.db.all(), q, queryVec);
|
|
61
|
+
// Refresh decay clocks for memories that surfaced (fire-and-forget).
|
|
62
|
+
for (const m of results)
|
|
63
|
+
this.db.touch(m.id).catch(() => { });
|
|
64
|
+
// Opportunistic decay pass, debounced (decision v3-Q1: piggyback on search).
|
|
65
|
+
this.maybeRunDecay();
|
|
22
66
|
const text = results.length === 0
|
|
23
67
|
? "No matching memories."
|
|
24
68
|
: results
|
|
@@ -27,7 +71,7 @@ export class MemoryService {
|
|
|
27
71
|
return { text, results };
|
|
28
72
|
}
|
|
29
73
|
async list(q) {
|
|
30
|
-
let memories = await this.db.all();
|
|
74
|
+
let memories = await this.db.all(q.includeArchived ?? false);
|
|
31
75
|
if (q.scope)
|
|
32
76
|
memories = memories.filter((m) => m.scope === q.scope || m.scope === "global");
|
|
33
77
|
if (q.type)
|
|
@@ -35,12 +79,188 @@ export class MemoryService {
|
|
|
35
79
|
memories.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
36
80
|
const text = memories.length === 0
|
|
37
81
|
? "No memories stored yet."
|
|
38
|
-
: memories
|
|
82
|
+
: memories
|
|
83
|
+
.map((m) => `[${m.id}]${m.archivedAt ? " [archived]" : ""} ${m.type} (${m.scope}): ${m.content.split("\n")[0]}`)
|
|
84
|
+
.join("\n");
|
|
39
85
|
return { text, memories };
|
|
40
86
|
}
|
|
41
87
|
async forget(id) {
|
|
42
88
|
const ok = await this.db.forget(id);
|
|
43
89
|
return { ok, text: ok ? `Deleted memory ${id}.` : `No memory with id ${id}.` };
|
|
44
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Session digest (v2) with contradiction-merge (v3):
|
|
93
|
+
* extract worth-keeping memories, skip exact duplicates, and let the LLM
|
|
94
|
+
* merge items that evolved from a stored memory (superseded text preserved).
|
|
95
|
+
*/
|
|
96
|
+
async digest(opts) {
|
|
97
|
+
const extracted = await this.extractFn(opts.transcript);
|
|
98
|
+
const active = await this.db.all();
|
|
99
|
+
const archived = await this.db.all(true).then((all) => all.filter((m) => m.archivedAt));
|
|
100
|
+
const seen = new Set(active.map((m) => dedupKey(m.type, m.content, m.scope)));
|
|
101
|
+
const stored = [];
|
|
102
|
+
let skippedDuplicates = 0;
|
|
103
|
+
let merged = 0;
|
|
104
|
+
for (const item of extracted) {
|
|
105
|
+
const scope = item.scope ?? opts.scope ?? "global";
|
|
106
|
+
const key = dedupKey(item.type, item.content, scope);
|
|
107
|
+
// Exact duplicate → skip (or revive if it had decayed).
|
|
108
|
+
if (seen.has(key)) {
|
|
109
|
+
skippedDuplicates++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const archivedDup = archived.find((m) => dedupKey(m.type, m.content, scope) === key);
|
|
113
|
+
if (archivedDup) {
|
|
114
|
+
const revived = await this.db.revive(archivedDup.id);
|
|
115
|
+
if (revived) {
|
|
116
|
+
seen.add(key);
|
|
117
|
+
merged++; // counted as a revival/refresh
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Evolved fact → LLM decides: store fresh, skip, or merge.
|
|
122
|
+
const itemVec = (await this.maybeEmbed(item.content)) ?? null;
|
|
123
|
+
const candidate = this.findCandidate(item, scope, [...active, ...archived], itemVec);
|
|
124
|
+
if (candidate) {
|
|
125
|
+
const decision = await this.mergeFn(item.content, {
|
|
126
|
+
type: candidate.type,
|
|
127
|
+
content: candidate.content,
|
|
128
|
+
});
|
|
129
|
+
if (decision.action === "skip") {
|
|
130
|
+
skippedDuplicates++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (decision.action === "merge") {
|
|
134
|
+
const now = new Date().toISOString().slice(0, 10);
|
|
135
|
+
const old = candidate.content.split("\n")[0];
|
|
136
|
+
const content = `${decision.content}\n\n> superseded (${now}): ${old}`;
|
|
137
|
+
const embedding = await this.maybeEmbed(decision.content);
|
|
138
|
+
await this.db.update({ ...candidate, content, embedding, source: opts.source ?? candidate.source });
|
|
139
|
+
merged++;
|
|
140
|
+
// Re-index dedup set against the new content.
|
|
141
|
+
seen.delete(dedupKey(candidate.type, candidate.content, candidate.scope));
|
|
142
|
+
seen.add(dedupKey(candidate.type, decision.content, candidate.scope));
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// action: "store" → fall through and store fresh
|
|
146
|
+
}
|
|
147
|
+
seen.add(key);
|
|
148
|
+
const { memory } = await this.store({
|
|
149
|
+
type: item.type,
|
|
150
|
+
content: item.content,
|
|
151
|
+
scope,
|
|
152
|
+
tags: item.tags,
|
|
153
|
+
importance: item.importance,
|
|
154
|
+
source: opts.source,
|
|
155
|
+
});
|
|
156
|
+
stored.push(memory);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
extracted: extracted.length,
|
|
160
|
+
stored,
|
|
161
|
+
skippedDuplicates,
|
|
162
|
+
merged,
|
|
163
|
+
ids: stored.map((m) => m.id),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Explicit maintenance (decision v3-Q1): decay sweep + vector backfill.
|
|
168
|
+
* Exposed as the `memory_maintain` tool, POST /maintain, and the CLI.
|
|
169
|
+
*/
|
|
170
|
+
async maintain() {
|
|
171
|
+
const result = await this.decayPass();
|
|
172
|
+
// Vector backfill: embed active memories stored while embeddings were off.
|
|
173
|
+
if (this.embedFn) {
|
|
174
|
+
const active = await this.db.all();
|
|
175
|
+
for (const m of active) {
|
|
176
|
+
if (m.embedding && m.embedding.length > 0)
|
|
177
|
+
continue;
|
|
178
|
+
const vec = await this.maybeEmbed(m.content);
|
|
179
|
+
if (vec) {
|
|
180
|
+
await this.db.update({ ...m, embedding: vec });
|
|
181
|
+
result.embedded++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
/** Decay lifecycle: unused actives → archived → auto-deleted past TTL. */
|
|
188
|
+
async decayPass() {
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
const archiveCutoff = now - this.archiveAfterDays * 86_400_000;
|
|
191
|
+
const ttlCutoff = now - this.archiveTtlDays * 86_400_000;
|
|
192
|
+
const result = { archived: [], deleted: [], embedded: 0 };
|
|
193
|
+
const active = await this.db.all();
|
|
194
|
+
for (const m of active) {
|
|
195
|
+
if (m.type === "role")
|
|
196
|
+
continue; // standing instructions never decay
|
|
197
|
+
const lastActive = Date.parse(m.lastSeen ?? m.updatedAt);
|
|
198
|
+
if (Number.isFinite(lastActive) && lastActive < archiveCutoff) {
|
|
199
|
+
await this.db.archive(m.id);
|
|
200
|
+
result.archived.push(m.id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const archived = (await this.db.all(true)).filter((m) => m.archivedAt);
|
|
204
|
+
for (const m of archived) {
|
|
205
|
+
const archivedAt = Date.parse(m.archivedAt);
|
|
206
|
+
if (Number.isFinite(archivedAt) && archivedAt < ttlCutoff) {
|
|
207
|
+
await this.db.forget(m.id);
|
|
208
|
+
result.deleted.push(m.id);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
maybeRunDecay() {
|
|
214
|
+
if (this.decayRunning)
|
|
215
|
+
return;
|
|
216
|
+
if (Date.now() - this.lastDecayRun < this.decayIntervalMs)
|
|
217
|
+
return;
|
|
218
|
+
this.lastDecayRun = Date.now();
|
|
219
|
+
this.decayRunning = true;
|
|
220
|
+
this.decayPass()
|
|
221
|
+
.catch((err) => console.error("Remembra: decay pass failed:", err))
|
|
222
|
+
.finally(() => {
|
|
223
|
+
this.decayRunning = false;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/** Pick the most similar stored memory of the same type+scope for merge comparison. */
|
|
227
|
+
findCandidate(item, scope, pool, itemVec) {
|
|
228
|
+
let best = null;
|
|
229
|
+
let bestScore = 0;
|
|
230
|
+
for (const m of pool) {
|
|
231
|
+
if (m.type !== item.type || m.scope !== scope)
|
|
232
|
+
continue;
|
|
233
|
+
const score = candidateSimilarity(item.content, m, itemVec);
|
|
234
|
+
if (score > bestScore) {
|
|
235
|
+
bestScore = score;
|
|
236
|
+
best = m;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Threshold: must be clearly about the same thing before spending an LLM call.
|
|
240
|
+
return bestScore >= 0.4 ? best : null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Normalized identity for exact-match dedup. */
|
|
244
|
+
function dedupKey(type, content, scope) {
|
|
245
|
+
return `${type}|${scope}|${content.toLowerCase().replace(/\s+/g, " ").trim()}`;
|
|
246
|
+
}
|
|
247
|
+
/** Similarity: embeddings (cosine) when both sides have vectors, else keyword overlap. */
|
|
248
|
+
function candidateSimilarity(newContent, m, itemVec) {
|
|
249
|
+
if (itemVec && m.embedding && m.embedding.length === itemVec.length) {
|
|
250
|
+
// Cosine is typically 0..1 for normalized-ish text vectors; scale to our 0..1 threshold.
|
|
251
|
+
return Math.max(0, cosine(itemVec, m.embedding));
|
|
252
|
+
}
|
|
253
|
+
const terms = new Set(newContent
|
|
254
|
+
.toLowerCase()
|
|
255
|
+
.split(/\s+/)
|
|
256
|
+
.filter((t) => t.length > 3));
|
|
257
|
+
if (terms.size === 0)
|
|
258
|
+
return 0;
|
|
259
|
+
const hay = m.content.toLowerCase();
|
|
260
|
+
let hits = 0;
|
|
261
|
+
for (const t of terms)
|
|
262
|
+
if (hay.includes(t))
|
|
263
|
+
hits++;
|
|
264
|
+
return hits / terms.size;
|
|
45
265
|
}
|
|
46
266
|
//# sourceMappingURL=service.js.map
|
package/dist/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import { Memory, StoreInput } from "./types.js";
|
|
2
2
|
/**
|
|
3
|
-
* File-based memory store.
|
|
3
|
+
* File-based memory store (source of truth — no database).
|
|
4
4
|
*
|
|
5
5
|
* Layout (default root: ~/.remembra):
|
|
6
|
-
* global/<id>.md — memories valid everywhere
|
|
7
|
-
* scopes/<scope>/<id>.md — memories scoped to a project/workspace
|
|
6
|
+
* global/<id>.md — active memories, valid everywhere
|
|
7
|
+
* scopes/<scope>/<id>.md — active memories scoped to a project/workspace
|
|
8
|
+
* archived/global/<id>.md — archived memories (out of search, listed with flag)
|
|
9
|
+
* archived/scopes/<scope>/ — archived scoped memories
|
|
8
10
|
*
|
|
9
|
-
* Each file is markdown with
|
|
10
|
-
*
|
|
11
|
+
* Each file is markdown with frontmatter for human readability and greppability.
|
|
12
|
+
* Maintenance runs opportunistically on search + via `memory_maintain`.
|
|
11
13
|
*/
|
|
12
14
|
export declare class MemoryStore {
|
|
13
15
|
private readonly root;
|
|
14
16
|
constructor(root: string);
|
|
15
17
|
static defaultRoot(): string;
|
|
16
18
|
private fileFor;
|
|
17
|
-
store(input: StoreInput): Promise<Memory>;
|
|
19
|
+
store(input: StoreInput, embedding?: number[]): Promise<Memory>;
|
|
18
20
|
forget(id: string): Promise<boolean>;
|
|
19
|
-
/** Load
|
|
20
|
-
all(): Promise<Memory[]>;
|
|
21
|
+
/** Load active memories (excludes archived). Pass includeArchived for everything. */
|
|
22
|
+
all(includeArchived?: boolean): Promise<Memory[]>;
|
|
21
23
|
get(id: string): Promise<Memory | null>;
|
|
24
|
+
/** Move a memory to the archived tree (sets archivedAt). */
|
|
25
|
+
archive(id: string): Promise<Memory | null>;
|
|
26
|
+
/** Bring an archived memory back into active search. */
|
|
27
|
+
revive(id: string): Promise<Memory | null>;
|
|
28
|
+
/** Persist changes to an existing memory (merge/update path). */
|
|
29
|
+
update(memory: Memory): Promise<Memory>;
|
|
30
|
+
/** Record that a memory surfaced in search (decay refresh). Cheap: no-op if seen <1h ago. */
|
|
31
|
+
touch(id: string): Promise<void>;
|
|
22
32
|
private findFile;
|
|
23
33
|
}
|