Skip to content

feat(agent-memory): Phase 5 — eviction & TTL#253

Open
jamby77 wants to merge 2 commits into
feature/agent-memory-phase4-reinforcefrom
feature/agent-memory-phase5-eviction
Open

feat(agent-memory): Phase 5 — eviction & TTL#253
jamby77 wants to merge 2 commits into
feature/agent-memory-phase4-reinforcefrom
feature/agent-memory-phase5-eviction

Conversation

@jamby77

@jamby77 jamby77 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #251 (Phase 4 — recall reinforcement).

What

Phase 5 of @betterdb/agent-memory: capacity-based eviction and TTL writes.

  • selectEvictions() (pure): selects the lowest-ranked keys to drop so maxItems remain. Ranks by a blend of importance and last-access recency — the recall weights minus similarity, renormalized — lowest first, ties broken toward the older last-access.
  • recencyDecay() extracted from compositeScore so recall ranking and eviction share one true half-life decay.
  • TTL writes: remember(content, { ttl }) sets the hash and its expiry atomically via MULTI/HSET/EXPIRE/EXEC; a non-positive/absent ttl writes a durable HSET.
  • Capacity enforcement: when maxItemsPerScope is configured, each write runs a count-first probe (cheap LIMIT 0 0) and only fetches candidates when over capacity, then evicts the excess and increments evictions in {name}:__mem_stats. Best-effort — a failed eviction pass never rejects an already-durable write.

Design notes

  • Eviction recency uses last_accessed_at (fed by reinforcement); recall recency uses created_at — deliberate split per spec.
  • Selection is exact while a scope fits EVICTION_SCAN_LIMIT; larger scopes evict from the scanned window and reclaim the remainder on subsequent writes.
  • TTL is a hard expiry (per spec "expire naturally") — reinforcement feeds recency/capacity-eviction, not TTL extension.

Tests

  • selectEvictions.test.ts — capacity boundary, drop count, importance- vs recency-driven ordering, weight-dominance, tie-breaks.
  • MemoryStore.eviction.test.ts — atomic TTL sequence, durable/non-positive-ttl paths, over/under-capacity eviction + counter, scope-filtered query, unconfigured no-op, best-effort failure isolation.

47/47 package tests green · tsc --noEmit clean · prettier clean.


Note

Medium Risk
Deletes stored memories on write when over capacity and changes remember's Redis command pattern; failures are isolated but eviction is destructive and approximate beyond the scan window.

Overview
Adds TTL and per-scope capacity limits to @betterdb/agent-memory MemoryStore.remember.

remember now accepts optional ttl (seconds). Positive TTL persists the hash and EXPIRE in one MULTI/EXEC; missing or non-positive TTL stays a plain durable HSET. After each write, optional maxItemsPerScope runs a best-effort capacity pass: a cheap FT.SEARCH count on the same scope/tag filter as recall/forget, then eviction only when over limit. Eviction uses new selectEvictions(), ranking by importance plus last_accessed_at recency (recall weights minus similarity, shared recencyDecay()), deletes losers, and increments {name}:__mem_stats evictions`. Enforcement failures are swallowed so writes still succeed.

Tests cover TTL command sequences, scope/tag partitioning, under-capacity no-ops, eviction selection, and write isolation when search fails.

Reviewed by Cursor Bugbot for commit a820313. Bugbot is set up for automated code reviews on this repo. Configure here.

@jamby77 jamby77 force-pushed the feature/agent-memory-phase4-reinforce branch from 8739aa6 to dcfde4b Compare June 18, 2026 06:57
@jamby77 jamby77 force-pushed the feature/agent-memory-phase5-eviction branch from 6e7c2e9 to 2dc4339 Compare June 18, 2026 06:57
Comment thread packages/agent-memory/src/MemoryStore.ts
- Add selectEvictions(): pure lowest-(importance, recency) selection,
  reusing the recall weights minus similarity, renormalized
- Extract recencyDecay() from compositeScore for shared half-life decay
- remember(): write expiring memories atomically via MULTI/HSET/EXPIRE/EXEC
  when ttl is set; durable HSET otherwise
- Enforce maxItemsPerScope on write (count-first probe, then bounded
  candidate scan) and record evictions in __mem_stats; best-effort so it
  never breaks the write path
- Add ttl to RememberOptions and maxItemsPerScope to MemoryStoreOptions
@jamby77 jamby77 force-pushed the feature/agent-memory-phase4-reinforce branch from dcfde4b to 38a6515 Compare June 18, 2026 07:19
@jamby77 jamby77 force-pushed the feature/agent-memory-phase5-eviction branch from 2dc4339 to e4646ec Compare June 18, 2026 07:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e4646ec. Configure here.

Comment thread packages/agent-memory/src/MemoryStore.ts Outdated
enforceCapacity built its filter with empty tags, so a tag-only write
collapsed to '*' and counted/evicted across the whole index instead of
the tag partition used by recall and forgetByScope. Pass the write tags
through so capacity is enforced on the same partition.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant