Skip to content

feat(agent-memory): Phase 8 — self-optimization config reads#256

Open
jamby77 wants to merge 3 commits into
feature/agent-memory-phase7-discoveryfrom
feature/agent-memory-phase8-config
Open

feat(agent-memory): Phase 8 — self-optimization config reads#256
jamby77 wants to merge 3 commits into
feature/agent-memory-phase7-discoveryfrom
feature/agent-memory-phase8-config

Conversation

@jamby77

@jamby77 jamby77 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #255 (Phase 7 — discovery marker).

What

Phase 8 of @betterdb/agent-memory: runtime-tunable recall/eviction knobs, so BetterDB Monitor's cache-proposals engine can retune a memory store without a restart (same pattern as semantic-cache's configRefresh).

MemoryStore reads {name}:__mem_config and live-applies:

  • recall.threshold (cosine-distance ceiling, 0..2)

  • recall.weights.similarity / recall.weights.recency / recall.weights.importance

  • recall.halfLifeSeconds

  • maxItemsPerScope

  • refreshConfig() (public, manual tick) reads the hash and applies it; currentConfig() exposes the effective tunables.

  • Opt-in configRefresh?: boolean | { enabled?, intervalMs? } enables an immediate read plus an unref'd interval (default 30s, min 1s); close() stops it.

Design / review notes

  • Off by default. Unlike semantic-cache (default-on), refresh is opt-in here so a standalone store never polls and existing unit tests keep their exact .call sequences.
  • Snapshot fallback. Each refresh rebuilds the config from the constructor values plus only the fields present in the hash, so removing a field reverts to the constructor default.
  • Validation. Invalid/out-of-range/NaN values are ignored; reads are best-effort and never throw. An all-zero weight vector is rejected (it would make every composite score 0 and leave recall ordering undefined) — kept from a review finding. initialWeights is copied so the shared DEFAULT_WEIGHTS constant can't be aliased.
  • recall() captures threshold+weights into locals before its first await, so an interval refresh can't tear an in-flight recall.

Tests (MemoryStore.config.test.ts, 12)

defaults snapshot · threshold · weights · halfLifeSeconds+maxItemsPerScope · partial config leaves others default · field-removal reverts · invalid ignored · all-zero weights rejected · live threshold affects recall end-to-end · no polling when disabled · immediate+interval read & stop on close (fake timers) · best-effort on read failure.

77/77 package tests green · tsc clean · prettier clean.


Note

Medium Risk
Changes live recall scoring, eviction victim selection, and capacity limits when external config is applied; validation and snapshots limit bad states, but mis-tuned remote values can still alter memory behavior at runtime.

Overview
Adds opt-in runtime tuning for MemoryStore by reading {name}:__mem_config from Valkey, mirroring semantic-cache’s configRefresh pattern so Monitor can retune recall/eviction without a restart.

refreshConfig() and currentConfig() expose manual reads and the effective snapshot (threshold, weights, half-life, maxItemsPerScope). With configRefresh enabled, the store polls on an interval (default 30s, min 1s), runs an immediate read at startup, and close() stops the timer. Refresh is off by default so existing stores never poll unless opted in.

Each refresh rebuilds from constructor defaults plus only fields present in the hash; removed fields revert. Invalid values are ignored; all-zero weight vectors are rejected. recall() and enforceCapacity() snapshot tunables before async work so concurrent refresh cannot mix config versions mid-operation.

New MemoryStore.config.test.ts coverage and an eviction test for mid-pass refresh; public types exported from index.ts.

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

@jamby77 jamby77 force-pushed the feature/agent-memory-phase8-config branch from eea7699 to ee647cc Compare June 17, 2026 14:50
Comment thread packages/agent-memory/src/MemoryStore.ts
@jamby77 jamby77 force-pushed the feature/agent-memory-phase7-discovery branch from 524f3a8 to e58dbb7 Compare June 18, 2026 06:57
@jamby77 jamby77 force-pushed the feature/agent-memory-phase8-config branch from 14b6e7c to 9412e5a Compare June 18, 2026 06:57
@jamby77 jamby77 force-pushed the feature/agent-memory-phase7-discovery branch from e58dbb7 to 0d89d67 Compare June 18, 2026 07:19
@jamby77 jamby77 force-pushed the feature/agent-memory-phase8-config branch from 9412e5a to 37c7a4b 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.

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 37c7a4b. Configure here.

Comment thread packages/agent-memory/src/MemoryStore.ts
jamby77 added 2 commits June 18, 2026 10:27
- MemoryStore reads {name}:__mem_config on an opt-in configRefresh
  interval and live-applies recall.threshold, recall.weights.*,
  recall.halfLifeSeconds, and maxItemsPerScope without a restart
- Absent fields fall back to constructor values; invalid values are
  ignored; reads are best-effort and never throw
- Reject an all-zero weight vector to keep recall ordering well-defined
- Add currentConfig() to expose the effective tunables; close() also
  stops the refresh interval
- Refresh is off by default (opt-in) so a standalone store never polls
Capture halfLifeSeconds with threshold/weights before the first await so a
concurrent configRefresh can't score a single recall with a mix of config
versions.
@jamby77 jamby77 force-pushed the feature/agent-memory-phase7-discovery branch from 0d89d67 to 73d91d7 Compare June 18, 2026 07:28
@jamby77 jamby77 force-pushed the feature/agent-memory-phase8-config branch from 37c7a4b to 06b56f6 Compare June 18, 2026 07:28
enforceCapacity read weights and halfLifeSeconds from instance fields at
selectEvictions time, several awaits after the capacity check. With opt-in
configRefresh a refresh could land mid-pass and score victims with a
different tunable set. Snapshot weights and halfLifeSeconds at entry,
matching the recall snapshot, so a pass uses one consistent config.
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