@eleboucher/pi-memini 0.5.6 → 0.5.8

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.
Files changed (2) hide show
  1. package/dist/index.js +33 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -348,6 +348,28 @@ export default function meminiExtension(pi) {
348
348
  // Assistant message ids already captured, so a re-fired agent_end never writes
349
349
  // a duplicate turn.
350
350
  const captured = new Set();
351
+ // Memory ids each session has already been shown (mirrors the openclaw
352
+ // plugin): the recall injection is a persistent context message, so
353
+ // re-injecting an unchanged match every turn stacks identical blocks in the
354
+ // prompt. Bounded so long-lived hosts can't grow the map without limit.
355
+ const injectedBySession = new Map();
356
+ const MAX_TRACKED_SESSIONS = 200;
357
+ const rememberInjected = (session, ids) => {
358
+ let seen = injectedBySession.get(session);
359
+ if (!seen) {
360
+ seen = new Set();
361
+ injectedBySession.set(session, seen);
362
+ while (injectedBySession.size > MAX_TRACKED_SESSIONS) {
363
+ const oldest = injectedBySession.keys().next().value;
364
+ if (oldest === undefined)
365
+ break;
366
+ injectedBySession.delete(oldest);
367
+ }
368
+ }
369
+ for (const id of ids)
370
+ if (id)
371
+ seen.add(id);
372
+ };
351
373
  // Recall before the turn: search for the user's prompt and inject the matches
352
374
  // as a persistent context message. Buffer the prompt for capture at agent_end.
353
375
  pi.on("before_agent_start", async (event, ctx) => {
@@ -366,7 +388,14 @@ export default function meminiExtension(pi) {
366
388
  body.min_score = cfg.recall_min_score;
367
389
  const result = await client.postJson("/v1/search", body);
368
390
  const floor = cfg.recall_min_score > 0 ? cfg.recall_min_score : 0;
369
- const rawHits = Array.isArray(result?.results) ? result.results : [];
391
+ let rawHits = Array.isArray(result?.results) ? result.results : [];
392
+ // Suppress memories this session has already been shown — the injected
393
+ // message persists in context, so a repeat adds nothing but noise.
394
+ if (sid) {
395
+ const seen = injectedBySession.get(sid);
396
+ if (seen?.size)
397
+ rawHits = rawHits.filter((r) => !seen.has(r?.memory?.id));
398
+ }
370
399
  const filtered = floor > 0
371
400
  ? rawHits.filter((r) => (typeof r?.score === "number" ? r.score : 0) >= floor)
372
401
  : rawHits;
@@ -376,6 +405,9 @@ export default function meminiExtension(pi) {
376
405
  const fit = fitByTokens(hits, cfg.recall_max_tokens);
377
406
  if (fit.items.length === 0)
378
407
  return;
408
+ if (sid) {
409
+ rememberInjected(sid, filtered.map((r) => r?.memory?.id).filter(Boolean));
410
+ }
379
411
  const lines = [
380
412
  "Relevant long-term memory from memini (background context — prefer " +
381
413
  "current workspace state and the user's instructions):",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleboucher/pi-memini",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Shared cross-session memory for the Pi coding agent, backed by a memini service.",
5
5
  "files": [
6
6
  "dist"