ahok-skill 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +8 -0
- package/Dockerfile +59 -0
- package/RAW_SKILL.md +219 -0
- package/README.md +277 -0
- package/SKILL.md +58 -0
- package/bin/opm.js +268 -0
- package/data/openmemory.sqlite +0 -0
- package/data/openmemory.sqlite-shm +0 -0
- package/data/openmemory.sqlite-wal +0 -0
- package/dist/ai/graph.js +293 -0
- package/dist/ai/mcp.js +397 -0
- package/dist/cli.js +78 -0
- package/dist/core/cfg.js +87 -0
- package/dist/core/db.js +636 -0
- package/dist/core/memory.js +116 -0
- package/dist/core/migrate.js +227 -0
- package/dist/core/models.js +105 -0
- package/dist/core/telemetry.js +57 -0
- package/dist/core/types.js +2 -0
- package/dist/core/vector/postgres.js +52 -0
- package/dist/core/vector/valkey.js +246 -0
- package/dist/core/vector_store.js +2 -0
- package/dist/index.js +44 -0
- package/dist/memory/decay.js +301 -0
- package/dist/memory/embed.js +675 -0
- package/dist/memory/hsg.js +959 -0
- package/dist/memory/reflect.js +131 -0
- package/dist/memory/user_summary.js +99 -0
- package/dist/migrate.js +9 -0
- package/dist/ops/compress.js +255 -0
- package/dist/ops/dynamics.js +189 -0
- package/dist/ops/extract.js +333 -0
- package/dist/ops/ingest.js +214 -0
- package/dist/server/index.js +109 -0
- package/dist/server/middleware/auth.js +137 -0
- package/dist/server/routes/auth.js +186 -0
- package/dist/server/routes/compression.js +108 -0
- package/dist/server/routes/dashboard.js +399 -0
- package/dist/server/routes/docs.js +241 -0
- package/dist/server/routes/dynamics.js +312 -0
- package/dist/server/routes/ide.js +280 -0
- package/dist/server/routes/index.js +33 -0
- package/dist/server/routes/keys.js +132 -0
- package/dist/server/routes/langgraph.js +61 -0
- package/dist/server/routes/memory.js +213 -0
- package/dist/server/routes/sources.js +140 -0
- package/dist/server/routes/system.js +63 -0
- package/dist/server/routes/temporal.js +293 -0
- package/dist/server/routes/users.js +101 -0
- package/dist/server/routes/vercel.js +57 -0
- package/dist/server/server.js +211 -0
- package/dist/server.js +3 -0
- package/dist/sources/base.js +223 -0
- package/dist/sources/github.js +171 -0
- package/dist/sources/google_drive.js +166 -0
- package/dist/sources/google_sheets.js +112 -0
- package/dist/sources/google_slides.js +139 -0
- package/dist/sources/index.js +34 -0
- package/dist/sources/notion.js +165 -0
- package/dist/sources/onedrive.js +143 -0
- package/dist/sources/web_crawler.js +166 -0
- package/dist/temporal_graph/index.js +20 -0
- package/dist/temporal_graph/query.js +240 -0
- package/dist/temporal_graph/store.js +116 -0
- package/dist/temporal_graph/timeline.js +241 -0
- package/dist/temporal_graph/types.js +2 -0
- package/dist/utils/chunking.js +60 -0
- package/dist/utils/index.js +31 -0
- package/dist/utils/keyword.js +94 -0
- package/dist/utils/text.js +120 -0
- package/nodemon.json +7 -0
- package/package.json +50 -0
- package/references/api_reference.md +66 -0
- package/references/examples.md +45 -0
- package/src/ai/graph.ts +363 -0
- package/src/ai/mcp.ts +494 -0
- package/src/cli.ts +94 -0
- package/src/core/cfg.ts +110 -0
- package/src/core/db.ts +1052 -0
- package/src/core/memory.ts +99 -0
- package/src/core/migrate.ts +302 -0
- package/src/core/models.ts +107 -0
- package/src/core/telemetry.ts +47 -0
- package/src/core/types.ts +130 -0
- package/src/core/vector/postgres.ts +61 -0
- package/src/core/vector/valkey.ts +261 -0
- package/src/core/vector_store.ts +9 -0
- package/src/index.ts +5 -0
- package/src/memory/decay.ts +427 -0
- package/src/memory/embed.ts +707 -0
- package/src/memory/hsg.ts +1245 -0
- package/src/memory/reflect.ts +158 -0
- package/src/memory/user_summary.ts +110 -0
- package/src/migrate.ts +8 -0
- package/src/ops/compress.ts +296 -0
- package/src/ops/dynamics.ts +272 -0
- package/src/ops/extract.ts +360 -0
- package/src/ops/ingest.ts +286 -0
- package/src/server/index.ts +159 -0
- package/src/server/middleware/auth.ts +156 -0
- package/src/server/routes/auth.ts +223 -0
- package/src/server/routes/compression.ts +106 -0
- package/src/server/routes/dashboard.ts +420 -0
- package/src/server/routes/docs.ts +380 -0
- package/src/server/routes/dynamics.ts +516 -0
- package/src/server/routes/ide.ts +283 -0
- package/src/server/routes/index.ts +32 -0
- package/src/server/routes/keys.ts +131 -0
- package/src/server/routes/langgraph.ts +71 -0
- package/src/server/routes/memory.ts +440 -0
- package/src/server/routes/sources.ts +111 -0
- package/src/server/routes/system.ts +68 -0
- package/src/server/routes/temporal.ts +335 -0
- package/src/server/routes/users.ts +111 -0
- package/src/server/routes/vercel.ts +55 -0
- package/src/server/server.js +215 -0
- package/src/server.ts +1 -0
- package/src/sources/base.ts +257 -0
- package/src/sources/github.ts +156 -0
- package/src/sources/google_drive.ts +144 -0
- package/src/sources/google_sheets.ts +85 -0
- package/src/sources/google_slides.ts +115 -0
- package/src/sources/index.ts +19 -0
- package/src/sources/notion.ts +148 -0
- package/src/sources/onedrive.ts +131 -0
- package/src/sources/web_crawler.ts +161 -0
- package/src/temporal_graph/index.ts +4 -0
- package/src/temporal_graph/query.ts +299 -0
- package/src/temporal_graph/store.ts +156 -0
- package/src/temporal_graph/timeline.ts +319 -0
- package/src/temporal_graph/types.ts +41 -0
- package/src/utils/chunking.ts +66 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/keyword.ts +137 -0
- package/src/utils/text.ts +115 -0
- package/tests/test_api_workspace_management.ts +413 -0
- package/tests/test_bulk_delete.ts +267 -0
- package/tests/test_omnibus.ts +166 -0
- package/tests/test_workspace_management.ts +278 -0
- package/tests/verify.ts +104 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { all_async, run_async, q, vector_store, memories_table } from "../core/db";
|
|
2
|
+
import { now } from "../utils";
|
|
3
|
+
import { env } from "../core/cfg";
|
|
4
|
+
|
|
5
|
+
type mem = {
|
|
6
|
+
id: string;
|
|
7
|
+
content?: string;
|
|
8
|
+
summary?: string;
|
|
9
|
+
vector: number[];
|
|
10
|
+
salience: number;
|
|
11
|
+
last_access: number;
|
|
12
|
+
state?: "hot" | "warm" | "cold";
|
|
13
|
+
coacts?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type decay_cfg = {
|
|
17
|
+
threads: number;
|
|
18
|
+
cold_threshold: number;
|
|
19
|
+
reinforce_on_query: boolean;
|
|
20
|
+
regeneration_enabled: boolean;
|
|
21
|
+
max_vec_dim: number;
|
|
22
|
+
min_vec_dim: number;
|
|
23
|
+
summary_layers: number;
|
|
24
|
+
lambda_hot: number;
|
|
25
|
+
lambda_warm: number;
|
|
26
|
+
lambda_cold: number;
|
|
27
|
+
time_unit_ms: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const parse_int = (x: any, d: number) =>
|
|
31
|
+
Number.isFinite(+x) ? Math.floor(+x) : d;
|
|
32
|
+
const parse_f = (x: any, d: number) => (Number.isFinite(+x) ? +x : d);
|
|
33
|
+
const parse_bool = (x: any, d: boolean) =>
|
|
34
|
+
x === "true" ? true : x === "false" ? false : d;
|
|
35
|
+
const clamp_f = (v: number, a: number, b: number) =>
|
|
36
|
+
Math.min(b, Math.max(a, v));
|
|
37
|
+
const clamp_i = (v: number, a: number, b: number) =>
|
|
38
|
+
Math.min(b, Math.max(a, Math.floor(v)));
|
|
39
|
+
const tick = () => new Promise<void>((r) => setImmediate(r));
|
|
40
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
41
|
+
|
|
42
|
+
const mean = (arr: number[]) =>
|
|
43
|
+
arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0;
|
|
44
|
+
const l2 = (v: number[]) => Math.sqrt(v.reduce((s, x) => s + x * x, 0));
|
|
45
|
+
const normalize = (v: number[]) => {
|
|
46
|
+
const n = l2(v) || 1;
|
|
47
|
+
for (let i = 0; i < v.length; i++) v[i] /= n;
|
|
48
|
+
return v;
|
|
49
|
+
};
|
|
50
|
+
const chunkz = <t>(arr: t[], k: number) => {
|
|
51
|
+
const n = Math.max(1, k | 0),
|
|
52
|
+
out: t[][] = Array.from({ length: n }, () => []);
|
|
53
|
+
for (let i = 0; i < arr.length; i++) out[i % n].push(arr[i]);
|
|
54
|
+
return out;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const make_decay_cfg = (): decay_cfg => ({
|
|
58
|
+
threads: parse_int(process.env.OM_DECAY_THREADS, 3),
|
|
59
|
+
cold_threshold: parse_f(process.env.OM_DECAY_COLD_THRESHOLD, 0.25),
|
|
60
|
+
reinforce_on_query: parse_bool(
|
|
61
|
+
process.env.OM_DECAY_REINFORCE_ON_QUERY,
|
|
62
|
+
true,
|
|
63
|
+
),
|
|
64
|
+
regeneration_enabled: parse_bool(process.env.OM_REGENERATION_ENABLED, true),
|
|
65
|
+
max_vec_dim: parse_int(process.env.OM_MAX_VECTOR_DIM, env.vec_dim || 1536),
|
|
66
|
+
min_vec_dim: parse_int(process.env.OM_MIN_VECTOR_DIM, 64),
|
|
67
|
+
summary_layers: clamp_i(parse_int(process.env.OM_SUMMARY_LAYERS, 3), 1, 3),
|
|
68
|
+
lambda_hot: 0.005,
|
|
69
|
+
lambda_warm: 0.02,
|
|
70
|
+
lambda_cold: 0.05,
|
|
71
|
+
time_unit_ms: 86_400_000,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const cfg = make_decay_cfg();
|
|
75
|
+
|
|
76
|
+
let active_q = 0;
|
|
77
|
+
let last_decay = 0;
|
|
78
|
+
const cooldown = 60000;
|
|
79
|
+
export const inc_q = () => active_q++;
|
|
80
|
+
export const dec_q = () => active_q--;
|
|
81
|
+
|
|
82
|
+
const pick_tier = (m: any, now_ts: number): "hot" | "warm" | "cold" => {
|
|
83
|
+
const dt = Math.max(0, now_ts - (m.last_seen_at || m.updated_at || now_ts));
|
|
84
|
+
const recent = dt < 6 * 86_400_000;
|
|
85
|
+
const high = (m.coactivations || 0) > 5 || (m.salience || 0) > 0.7;
|
|
86
|
+
if (recent && high) return "hot";
|
|
87
|
+
if (recent || (m.salience || 0) > 0.4) return "warm";
|
|
88
|
+
return "cold";
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const compress_vector = (
|
|
92
|
+
vec: number[],
|
|
93
|
+
f: number,
|
|
94
|
+
min_dim = 64,
|
|
95
|
+
max_dim = 1536,
|
|
96
|
+
): number[] => {
|
|
97
|
+
const src = vec.length ? vec : [1];
|
|
98
|
+
const tgt_dim = Math.max(
|
|
99
|
+
min_dim,
|
|
100
|
+
Math.min(max_dim, Math.floor(src.length * clamp_f(f, 0.0, 1.0))),
|
|
101
|
+
);
|
|
102
|
+
const dim = Math.max(min_dim, Math.min(src.length, tgt_dim));
|
|
103
|
+
if (dim >= src.length) return src.slice(0);
|
|
104
|
+
const pooled: number[] = [];
|
|
105
|
+
const bucket = Math.ceil(src.length / dim);
|
|
106
|
+
for (let i = 0; i < src.length; i += bucket)
|
|
107
|
+
pooled.push(mean(src.slice(i, i + bucket)));
|
|
108
|
+
normalize(pooled);
|
|
109
|
+
return pooled;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const compress_summary = (txt: string, f: number, layers = 3): string => {
|
|
113
|
+
const t = (txt || "").trim();
|
|
114
|
+
if (!t) return "";
|
|
115
|
+
const lay = clamp_i(layers, 1, 3);
|
|
116
|
+
const trunc = (s: string, n: number) =>
|
|
117
|
+
s.length <= n ? s : s.slice(0, n - 1).trimEnd() + "…";
|
|
118
|
+
const sumz = (s: string) => summarize_quick(s);
|
|
119
|
+
const keys = (s: string, k = 5) => top_keywords(s, k).join(" ");
|
|
120
|
+
if (f > 0.8) return trunc(t, 200);
|
|
121
|
+
if (f > 0.4) return trunc(sumz(t), lay >= 2 ? 80 : 200);
|
|
122
|
+
return keys(t, lay >= 3 ? 5 : 3);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const fingerprint_mem = (m: any): { vector: number[]; summary: string } => {
|
|
126
|
+
const base = (m.id + "|" + (m.summary || m.content || "")).trim();
|
|
127
|
+
const vec = hash_to_vec(base, 32);
|
|
128
|
+
normalize(vec);
|
|
129
|
+
const summary = top_keywords(m.summary || m.content || "", 3).join(" ");
|
|
130
|
+
return { vector: vec, summary };
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const hash_to_vec = (s: string, d = 32): number[] => {
|
|
134
|
+
let h = 2166136261 >>> 0;
|
|
135
|
+
for (let i = 0; i < s.length; i++) {
|
|
136
|
+
h ^= s.charCodeAt(i);
|
|
137
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
138
|
+
}
|
|
139
|
+
const out: number[] = new Array(Math.max(2, d | 0)).fill(0);
|
|
140
|
+
let x = h || 1;
|
|
141
|
+
for (let i = 0; i < out.length; i++) {
|
|
142
|
+
x ^= x << 13;
|
|
143
|
+
x ^= x >>> 17;
|
|
144
|
+
x ^= x << 5;
|
|
145
|
+
out[i] = ((x >>> 0) / 0xffffffff) * 2 - 1;
|
|
146
|
+
}
|
|
147
|
+
normalize(out);
|
|
148
|
+
return out;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const summarize_quick = (t: string): string => {
|
|
152
|
+
const sents = t.split(/(?<=[.!?])\s+/).filter(Boolean);
|
|
153
|
+
if (!sents.length) return t;
|
|
154
|
+
const score = (s: string) =>
|
|
155
|
+
top_keywords(s, 6).length + Math.min(3, s.match(/[,;:]/g)?.length || 0);
|
|
156
|
+
const top = sents
|
|
157
|
+
.map((s, i) => ({ s, i, sc: score(s) }))
|
|
158
|
+
.sort((a, b) => b.sc - a.sc || a.i - b.i)
|
|
159
|
+
.slice(0, Math.min(3, Math.ceil(sents.length / 3)))
|
|
160
|
+
.sort((a, b) => a.i - b.i)
|
|
161
|
+
.map((x) => x.s)
|
|
162
|
+
.join(" ");
|
|
163
|
+
return top || sents[0];
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const stop = new Set([
|
|
167
|
+
"the",
|
|
168
|
+
"a",
|
|
169
|
+
"an",
|
|
170
|
+
"to",
|
|
171
|
+
"of",
|
|
172
|
+
"and",
|
|
173
|
+
"or",
|
|
174
|
+
"in",
|
|
175
|
+
"on",
|
|
176
|
+
"for",
|
|
177
|
+
"with",
|
|
178
|
+
"at",
|
|
179
|
+
"by",
|
|
180
|
+
"is",
|
|
181
|
+
"it",
|
|
182
|
+
"be",
|
|
183
|
+
"as",
|
|
184
|
+
"are",
|
|
185
|
+
"was",
|
|
186
|
+
"were",
|
|
187
|
+
"from",
|
|
188
|
+
"that",
|
|
189
|
+
"this",
|
|
190
|
+
"these",
|
|
191
|
+
"those",
|
|
192
|
+
"but",
|
|
193
|
+
"if",
|
|
194
|
+
"then",
|
|
195
|
+
"so",
|
|
196
|
+
"than",
|
|
197
|
+
"into",
|
|
198
|
+
"over",
|
|
199
|
+
"under",
|
|
200
|
+
"about",
|
|
201
|
+
"via",
|
|
202
|
+
"vs",
|
|
203
|
+
"not",
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
const top_keywords = (t: string, k = 5): string[] => {
|
|
207
|
+
const words = (t.toLowerCase().match(/[a-z0-9]+/g) || []).filter(
|
|
208
|
+
(w) => !stop.has(w),
|
|
209
|
+
);
|
|
210
|
+
if (!words.length) return [];
|
|
211
|
+
const freq = new Map<string, number>();
|
|
212
|
+
for (const w of words) freq.set(w, (freq.get(w) || 0) + 1);
|
|
213
|
+
return Array.from(freq.entries())
|
|
214
|
+
.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1))
|
|
215
|
+
.slice(0, k)
|
|
216
|
+
.map(([w]) => w);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export const apply_decay = async () => {
|
|
220
|
+
if (active_q > 0) {
|
|
221
|
+
console.log(`[decay] skipped - ${active_q} active queries`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const now_ts = Date.now();
|
|
225
|
+
if (now_ts - last_decay < cooldown) {
|
|
226
|
+
console.log(
|
|
227
|
+
`[decay] skipped - cooldown active (${((cooldown - (now_ts - last_decay)) / 1000).toFixed(0)}s remaining)`,
|
|
228
|
+
);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
last_decay = now_ts;
|
|
232
|
+
const t0 = performance.now();
|
|
233
|
+
|
|
234
|
+
const segments = await q.get_segments.all();
|
|
235
|
+
let tot_proc = 0,
|
|
236
|
+
tot_chg = 0,
|
|
237
|
+
tot_comp = 0,
|
|
238
|
+
tot_fp = 0;
|
|
239
|
+
const tier_counts = { hot: 0, warm: 0, cold: 0 };
|
|
240
|
+
|
|
241
|
+
for (const seg of segments) {
|
|
242
|
+
const segment = seg.segment;
|
|
243
|
+
const rows = await all_async(
|
|
244
|
+
"select id,content,summary,salience,decay_lambda,last_seen_at,updated_at,primary_sector,coactivations from memories where segment=?",
|
|
245
|
+
[segment],
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const decay_ratio = env.decay_ratio;
|
|
249
|
+
const batch_sz = Math.max(1, Math.floor(rows.length * decay_ratio));
|
|
250
|
+
const start_idx = Math.floor(
|
|
251
|
+
Math.random() * Math.max(1, rows.length - batch_sz + 1),
|
|
252
|
+
);
|
|
253
|
+
const batch = rows.slice(start_idx, start_idx + batch_sz);
|
|
254
|
+
|
|
255
|
+
const parts = chunkz(batch, cfg.threads);
|
|
256
|
+
|
|
257
|
+
await Promise.all(
|
|
258
|
+
parts.map(async (part) => {
|
|
259
|
+
for (const m of part) {
|
|
260
|
+
const tier = pick_tier(m, now_ts);
|
|
261
|
+
tier_counts[tier]++;
|
|
262
|
+
|
|
263
|
+
const lam =
|
|
264
|
+
tier === "hot"
|
|
265
|
+
? cfg.lambda_hot
|
|
266
|
+
: tier === "warm"
|
|
267
|
+
? cfg.lambda_warm
|
|
268
|
+
: cfg.lambda_cold;
|
|
269
|
+
const dt = Math.max(
|
|
270
|
+
0,
|
|
271
|
+
(now_ts - (m.last_seen_at || m.updated_at)) /
|
|
272
|
+
cfg.time_unit_ms,
|
|
273
|
+
);
|
|
274
|
+
const act = Math.max(0, m.coactivations || 0);
|
|
275
|
+
const sal = clamp_f(
|
|
276
|
+
(m.salience || 0.5) * (1 + Math.log1p(act)),
|
|
277
|
+
0,
|
|
278
|
+
1,
|
|
279
|
+
);
|
|
280
|
+
const f = Math.exp(-lam * (dt / (sal + 0.1)));
|
|
281
|
+
|
|
282
|
+
let new_sal = clamp_f(sal * f, 0, 1);
|
|
283
|
+
let changed = Math.abs(new_sal - m.salience) > 0.001;
|
|
284
|
+
let compressed = false;
|
|
285
|
+
let fingerprinted = false;
|
|
286
|
+
|
|
287
|
+
if (f < 0.7) {
|
|
288
|
+
const sector = m.primary_sector || "semantic";
|
|
289
|
+
const vec_row = await vector_store.getVector(m.id, sector);
|
|
290
|
+
|
|
291
|
+
if (vec_row && vec_row.vector) {
|
|
292
|
+
const vec =
|
|
293
|
+
typeof vec_row.vector === "string"
|
|
294
|
+
? JSON.parse(vec_row.vector)
|
|
295
|
+
: vec_row.vector;
|
|
296
|
+
const before_len = Array.isArray(vec)
|
|
297
|
+
? vec.length
|
|
298
|
+
: 0;
|
|
299
|
+
|
|
300
|
+
if (before_len > 0) {
|
|
301
|
+
const new_vec = compress_vector(
|
|
302
|
+
vec,
|
|
303
|
+
f,
|
|
304
|
+
cfg.min_vec_dim,
|
|
305
|
+
cfg.max_vec_dim,
|
|
306
|
+
);
|
|
307
|
+
const new_summary = compress_summary(
|
|
308
|
+
m.summary || m.content || "",
|
|
309
|
+
f,
|
|
310
|
+
cfg.summary_layers,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
if (new_vec.length < before_len) {
|
|
314
|
+
await vector_store.storeVector(
|
|
315
|
+
m.id,
|
|
316
|
+
sector,
|
|
317
|
+
new_vec,
|
|
318
|
+
new_vec.length,
|
|
319
|
+
);
|
|
320
|
+
compressed = true;
|
|
321
|
+
tot_comp++;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (new_summary !== (m.summary || "")) {
|
|
325
|
+
await run_async(
|
|
326
|
+
"update memories set summary=? where id=?",
|
|
327
|
+
[new_summary, m.id],
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
changed = true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (f < Math.max(0.3, cfg.cold_threshold)) {
|
|
336
|
+
const sector = m.primary_sector || "semantic";
|
|
337
|
+
const fp = fingerprint_mem(m);
|
|
338
|
+
await vector_store.storeVector(
|
|
339
|
+
m.id,
|
|
340
|
+
sector,
|
|
341
|
+
fp.vector,
|
|
342
|
+
fp.vector.length,
|
|
343
|
+
);
|
|
344
|
+
await run_async(
|
|
345
|
+
"update memories set summary=? where id=?",
|
|
346
|
+
[fp.summary, m.id],
|
|
347
|
+
);
|
|
348
|
+
fingerprinted = true;
|
|
349
|
+
tot_fp++;
|
|
350
|
+
changed = true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (changed) {
|
|
354
|
+
await run_async(
|
|
355
|
+
`update ${memories_table} set salience=?,updated_at=? where id=?`,
|
|
356
|
+
[new_sal, now(), m.id],
|
|
357
|
+
);
|
|
358
|
+
tot_chg++;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
tot_proc++;
|
|
362
|
+
await tick();
|
|
363
|
+
}
|
|
364
|
+
}),
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
if (seg !== segments[segments.length - 1]) {
|
|
368
|
+
await sleep(env.decay_sleep_ms);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const tot = performance.now() - t0;
|
|
373
|
+
// Use stderr for debug output to avoid breaking MCP JSON-RPC protocol
|
|
374
|
+
console.error(
|
|
375
|
+
`[decay-2.0] ${tot_chg}/${tot_proc} | tiers: hot=${tier_counts.hot} warm=${tier_counts.warm} cold=${tier_counts.cold} | compressed=${tot_comp} fingerprinted=${tot_fp} | ${tot.toFixed(1)}ms across ${segments.length} segments`,
|
|
376
|
+
);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
export const on_query_hit = async (
|
|
380
|
+
mem_id: string,
|
|
381
|
+
sector: string,
|
|
382
|
+
reembed?: (text: string) => Promise<number[]>,
|
|
383
|
+
) => {
|
|
384
|
+
if (!cfg.regeneration_enabled && !cfg.reinforce_on_query) return;
|
|
385
|
+
|
|
386
|
+
const m = await q.get_mem.get(mem_id);
|
|
387
|
+
if (!m) return;
|
|
388
|
+
|
|
389
|
+
let updated = false;
|
|
390
|
+
|
|
391
|
+
if (cfg.regeneration_enabled && reembed) {
|
|
392
|
+
const vec_row = await vector_store.getVector(mem_id, sector);
|
|
393
|
+
if (vec_row && vec_row.vector) {
|
|
394
|
+
const vec =
|
|
395
|
+
typeof vec_row.vector === "string"
|
|
396
|
+
? JSON.parse(vec_row.vector)
|
|
397
|
+
: vec_row.vector;
|
|
398
|
+
if (Array.isArray(vec) && vec.length <= 64) {
|
|
399
|
+
try {
|
|
400
|
+
const base = m.summary || m.content || "";
|
|
401
|
+
const new_vec = await reembed(base);
|
|
402
|
+
await vector_store.storeVector(
|
|
403
|
+
mem_id,
|
|
404
|
+
sector,
|
|
405
|
+
new_vec,
|
|
406
|
+
new_vec.length,
|
|
407
|
+
);
|
|
408
|
+
updated = true;
|
|
409
|
+
} catch (e) { }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (cfg.reinforce_on_query) {
|
|
415
|
+
const new_sal = clamp_f((m.salience || 0.5) + 0.5, 0, 1);
|
|
416
|
+
await run_async(
|
|
417
|
+
`update ${memories_table} set salience=?,last_seen_at=? where id=?`,
|
|
418
|
+
[new_sal, now(), mem_id],
|
|
419
|
+
);
|
|
420
|
+
updated = true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (updated) {
|
|
424
|
+
// Use stderr for debug output to avoid breaking MCP JSON-RPC protocol
|
|
425
|
+
console.error(`[decay-2.0] regenerated/reinforced memory ${mem_id}`);
|
|
426
|
+
}
|
|
427
|
+
};
|