fix(memory): warn when MEMORY.md / USER.md exceed their char limit on load
The cap only fires on add/replace, so an externally written over-budget file rode silently in the system prompt while every later add was refused with no visible cause. Warn at load; entries stay loaded (never truncate a user's memories). Salvage of #10886 (original hunk targeted memory_tool.py before the store split); authored by @easyvibecoding. Refs #10877
This commit is contained in:
@@ -270,6 +270,26 @@ class TestMemoryStorePersistence:
|
||||
assert len(store.memory_entries) == 2
|
||||
|
||||
|
||||
class TestMemoryStoreCharLimitOnLoad:
|
||||
@pytest.mark.parametrize("filename, target", [("MEMORY.md", "memory"), ("USER.md", "user")])
|
||||
def test_over_limit_file_loads_but_warns(self, tmp_path, monkeypatch, caplog, filename, target):
|
||||
"""An externally written over-budget file is kept (no silent data loss) and named in a
|
||||
warning; an in-budget file loads quietly (#10877)."""
|
||||
import logging
|
||||
monkeypatch.setattr("tools.memory_tool.get_memory_dir", lambda: tmp_path)
|
||||
(tmp_path / filename).write_text("x" * 600, encoding="utf-8")
|
||||
with caplog.at_level(logging.WARNING):
|
||||
store = MemoryStore(memory_char_limit=500, user_char_limit=300)
|
||||
store.load_from_disk()
|
||||
assert filename in caplog.text and "exceeds" in caplog.text
|
||||
assert len(store._entries_for(target)) == 1
|
||||
caplog.clear()
|
||||
(tmp_path / filename).write_text("short", encoding="utf-8")
|
||||
with caplog.at_level(logging.WARNING):
|
||||
MemoryStore(memory_char_limit=500, user_char_limit=300).load_from_disk()
|
||||
assert "exceeds" not in caplog.text
|
||||
|
||||
|
||||
class TestMemoryStoreSnapshot:
|
||||
def test_snapshot_frozen_at_load(self, store):
|
||||
assert store.format_for_system_prompt("memory") is None # empty store
|
||||
|
||||
@@ -130,6 +130,13 @@ class MemoryStore:
|
||||
# Deduplicate (order-preserving, first occurrence wins).
|
||||
entries = list(dict.fromkeys(self._read_file(path)))
|
||||
self._set_entries(target, entries)
|
||||
# External writers (MCP bridges, hand edits) can exceed the cap; the limit only fires on
|
||||
# add/replace, so the oversized block would silently ride in the prompt while every later
|
||||
# add is refused with no visible cause (#10877). Warn; never truncate a user's memories.
|
||||
if (count := self._char_count(target)) > (limit := self._char_limit(target)):
|
||||
logger.warning("%s exceeds its char limit on load: %d/%d chars. Entries stay loaded; "
|
||||
"further additions are blocked until it is back under the limit.",
|
||||
path.name, count, limit)
|
||||
self._system_prompt_snapshot[target] = self._render_block(target, [_sanitize(e, path.name) for e in entries])
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user