@eleboucher/pi-memini 0.5.7 → 0.5.9
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/dist/index.js +38 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -229,8 +229,12 @@ function createClient(cfg, warn) {
|
|
|
229
229
|
signal: AbortSignal.timeout(cfg.timeout_ms),
|
|
230
230
|
});
|
|
231
231
|
if (!res.ok) {
|
|
232
|
-
if (cfg.fallback_on_error)
|
|
232
|
+
if (cfg.fallback_on_error) {
|
|
233
|
+
// Degrade but never silently: a swallowed 401/500 on a capture or
|
|
234
|
+
// recall looks like "memory isn't working" with nothing to debug.
|
|
235
|
+
warn(`memini ${method} ${path} failed: ${res.status}`);
|
|
233
236
|
return null;
|
|
237
|
+
}
|
|
234
238
|
const text = await res.text().catch(() => "");
|
|
235
239
|
throw new Error(`memini ${method} ${path} failed: ${res.status} ${text}`);
|
|
236
240
|
}
|
|
@@ -348,6 +352,28 @@ export default function meminiExtension(pi) {
|
|
|
348
352
|
// Assistant message ids already captured, so a re-fired agent_end never writes
|
|
349
353
|
// a duplicate turn.
|
|
350
354
|
const captured = new Set();
|
|
355
|
+
// Memory ids each session has already been shown (mirrors the openclaw
|
|
356
|
+
// plugin): the recall injection is a persistent context message, so
|
|
357
|
+
// re-injecting an unchanged match every turn stacks identical blocks in the
|
|
358
|
+
// prompt. Bounded so long-lived hosts can't grow the map without limit.
|
|
359
|
+
const injectedBySession = new Map();
|
|
360
|
+
const MAX_TRACKED_SESSIONS = 200;
|
|
361
|
+
const rememberInjected = (session, ids) => {
|
|
362
|
+
let seen = injectedBySession.get(session);
|
|
363
|
+
if (!seen) {
|
|
364
|
+
seen = new Set();
|
|
365
|
+
injectedBySession.set(session, seen);
|
|
366
|
+
while (injectedBySession.size > MAX_TRACKED_SESSIONS) {
|
|
367
|
+
const oldest = injectedBySession.keys().next().value;
|
|
368
|
+
if (oldest === undefined)
|
|
369
|
+
break;
|
|
370
|
+
injectedBySession.delete(oldest);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const id of ids)
|
|
374
|
+
if (id)
|
|
375
|
+
seen.add(id);
|
|
376
|
+
};
|
|
351
377
|
// Recall before the turn: search for the user's prompt and inject the matches
|
|
352
378
|
// as a persistent context message. Buffer the prompt for capture at agent_end.
|
|
353
379
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -366,7 +392,14 @@ export default function meminiExtension(pi) {
|
|
|
366
392
|
body.min_score = cfg.recall_min_score;
|
|
367
393
|
const result = await client.postJson("/v1/search", body);
|
|
368
394
|
const floor = cfg.recall_min_score > 0 ? cfg.recall_min_score : 0;
|
|
369
|
-
|
|
395
|
+
let rawHits = Array.isArray(result?.results) ? result.results : [];
|
|
396
|
+
// Suppress memories this session has already been shown — the injected
|
|
397
|
+
// message persists in context, so a repeat adds nothing but noise.
|
|
398
|
+
if (sid) {
|
|
399
|
+
const seen = injectedBySession.get(sid);
|
|
400
|
+
if (seen?.size)
|
|
401
|
+
rawHits = rawHits.filter((r) => !seen.has(r?.memory?.id));
|
|
402
|
+
}
|
|
370
403
|
const filtered = floor > 0
|
|
371
404
|
? rawHits.filter((r) => (typeof r?.score === "number" ? r.score : 0) >= floor)
|
|
372
405
|
: rawHits;
|
|
@@ -376,6 +409,9 @@ export default function meminiExtension(pi) {
|
|
|
376
409
|
const fit = fitByTokens(hits, cfg.recall_max_tokens);
|
|
377
410
|
if (fit.items.length === 0)
|
|
378
411
|
return;
|
|
412
|
+
if (sid) {
|
|
413
|
+
rememberInjected(sid, filtered.map((r) => r?.memory?.id).filter(Boolean));
|
|
414
|
+
}
|
|
379
415
|
const lines = [
|
|
380
416
|
"Relevant long-term memory from memini (background context — prefer " +
|
|
381
417
|
"current workspace state and the user's instructions):",
|