feat: add ignored_memory_patterns config option for regex-based memory exclusion (#1145)

Co-authored-by: Bokai Yang <22804145+YouJianFengXue@users.noreply.github.com>
Co-authored-by: Michael Panchenko <35432522+MischaPanch@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-03-23 18:12:30 +01:00
1 parent bf92f3cb49
commit b898677b1c
6 files changed
+79 -2

No files matched your search

+7
View File
@@ -2,6 +2,13 @@
Status of the `main` branch. Changes prior to the next official version change will appear here.
* Memories:
* Add `ignored_memory_patterns` configuration option (regex-based) to completely exclude
matching memories from listing, reading, and writing. Configurable at both global
(`serena_config.yml`) and project (`project.yml`) level, merged additively.
Useful for projects with large numbers of archived memory files that inflate
`activate_project` output.
* New language support:
* Add Solidity language server support (`Language.SOLIDITY`) using the
Nomic Foundation `@nomicfoundation/solidity-language-server`. Automatically
+16
View File
@@ -39,6 +39,22 @@ Since global memories are not versioned alongside your project files,
it can be helpful to track global memories with git (i.e. to make `~/.serena/memories/` a git repository)
in order to have a history of changes and the possibility to revert them if needed.
### Ignoring Memories
Projects that accumulate large numbers of archived memory files can use `ignored_memory_patterns`
to exclude them from `list_memories` and `activate_project` output. Add regex patterns to the
global or project-level [configuration](050_configuration):
```yaml
ignored_memory_patterns: ["_archive/.*", "_episodes/.*"]
```
Ignored memories are completely excluded — they cannot be accessed via `read_memory`, `write_memory`,
or any other memory tool. To read an ignored memory file, use the `read_file` tool on the raw file path
(e.g., `.serena/memories/_archive/2026-03/some-topic.md`).
Like `read_only_memory_patterns`, patterns from the global and project-level configurations are merged additively.
### Manually Editing Memories
You may edit memories directly in the file system, using your preferred text editor or IDE.
+2
View File
@@ -222,6 +222,7 @@ class SharedConfig(ModeSelectionDefinition, ToolInclusionDefinition, ToStringMix
language_backend: LanguageBackend | None = None
line_ending: LineEnding | None = None
read_only_memory_patterns: list[str] = field(default_factory=list)
ignored_memory_patterns: list[str] = field(default_factory=list)
ls_specific_settings: dict = field(default_factory=dict)
"""Advanced configuration option allowing to configure language server implementation specific options, see SolidLSPSettings for more info."""
@@ -453,6 +454,7 @@ class ProjectConfig(SharedConfig):
included_optional_tools=data["included_optional_tools"],
read_only=data["read_only"],
read_only_memory_patterns=data.get("read_only_memory_patterns", []),
ignored_memory_patterns=data.get("ignored_memory_patterns", []),
ignore_all_files_in_gitignore=data["ignore_all_files_in_gitignore"],
initial_prompt=data["initial_prompt"],
encoding=data["encoding"],
+37 -2
View File
@@ -35,10 +35,18 @@ class MemoriesManager:
GLOBAL_TOPIC = "global"
_global_memory_dir = SerenaPaths().global_memories_path
def __init__(self, serena_data_folder: str | Path | None, read_only_memory_patterns: Sequence[str] = ()):
def __init__(
self,
serena_data_folder: str | Path | None,
read_only_memory_patterns: Sequence[str] = (),
ignored_memory_patterns: Sequence[str] = (),
):
"""
:param serena_data_folder: the absolute path to the project's .serena data folder
:param read_only_memory_patterns: whether to allow writing global memories in tool execution contexts
:param ignored_memory_patterns: regex patterns for memories to completely exclude from listing, reading, and writing.
Matching memories will not appear in list_memories or activate_project output and cannot be accessed
via read_memory or write_memory. Use read_file on the raw path to access ignored memory files.
"""
self._project_memory_dir: Path | None = None
if serena_data_folder is not None:
@@ -46,6 +54,7 @@ class MemoriesManager:
self._project_memory_dir.mkdir(parents=True, exist_ok=True)
self._encoding = SERENA_FILE_ENCODING
self._read_only_memory_patterns = [re.compile(pattern) for pattern in set(read_only_memory_patterns)]
self._ignored_memory_patterns = [re.compile(pattern) for pattern in set(ignored_memory_patterns)]
def _is_read_only_memory(self, name: str) -> bool:
for pattern in self._read_only_memory_patterns:
@@ -53,6 +62,19 @@ class MemoriesManager:
return True
return False
def _is_ignored_memory(self, name: str) -> bool:
for pattern in self._ignored_memory_patterns:
if pattern.fullmatch(name):
return True
return False
def _check_not_ignored(self, name: str) -> None:
if self._is_ignored_memory(name):
raise ValueError(
f"Memory '{name}' matches an ignored_memory_patterns pattern and cannot be accessed. "
f"Use the read_file tool on the raw file path instead."
)
def _is_global(self, name: str) -> bool:
return name == self.GLOBAL_TOPIC or name.startswith(self.GLOBAL_TOPIC + "/")
@@ -95,6 +117,7 @@ class MemoriesManager:
raise PermissionError(f"Attempted to write to read_only memory: '{name}')")
def load_memory(self, name: str) -> str:
self._check_not_ignored(name)
memory_file_path = self.get_memory_file_path(name)
if not memory_file_path.exists():
return f"Memory file {name} not found, consider creating it with the `write_memory` tool if you need it."
@@ -102,6 +125,7 @@ class MemoriesManager:
return f.read()
def save_memory(self, name: str, content: str, is_tool_context: bool) -> str:
self._check_not_ignored(name)
self._check_write_access(name, is_tool_context)
memory_file_path = self.get_memory_file_path(name)
with open(memory_file_path, "w", encoding=self._encoding) as f:
@@ -144,6 +168,8 @@ class MemoriesManager:
for md_file in search_dir.rglob("*.md"):
rel = str(md_file.relative_to(base_dir).with_suffix("")).replace(os.sep, "/")
memory_name = prefix + rel
if self._is_ignored_memory(memory_name):
continue
result.add(memory_name, is_read_only=self._is_read_only_memory(memory_name))
return result
@@ -181,6 +207,7 @@ class MemoriesManager:
return memories
def delete_memory(self, name: str, is_tool_context: bool) -> str:
self._check_not_ignored(name)
self._check_write_access(name, is_tool_context)
memory_file_path = self.get_memory_file_path(name)
if not memory_file_path.exists():
@@ -193,6 +220,8 @@ class MemoriesManager:
Rename or move a memory file.
Moving between global and project scope (e.g. "global/foo" -> "bar") is supported.
"""
self._check_not_ignored(old_name)
self._check_not_ignored(new_name)
self._check_write_access(new_name, is_tool_context)
old_path = self.get_memory_file_path(old_name)
@@ -220,6 +249,7 @@ class MemoriesManager:
:param mode: "literal" or "regex"
:param allow_multiple_occurrences:
"""
self._check_not_ignored(name)
self._check_write_access(name, is_tool_context)
memory_file_path = self.get_memory_file_path(name)
if not memory_file_path.exists():
@@ -250,7 +280,12 @@ class Project(ToStringMixin):
log.info("Serena project data folder: %s", self._serena_data_folder)
read_only_memory_patterns = serena_config.read_only_memory_patterns + project_config.read_only_memory_patterns
self.memories_manager = MemoriesManager(self._serena_data_folder, read_only_memory_patterns=read_only_memory_patterns)
ignored_memory_patterns = serena_config.ignored_memory_patterns + project_config.ignored_memory_patterns
self.memories_manager = MemoriesManager(
self._serena_data_folder,
read_only_memory_patterns=read_only_memory_patterns,
ignored_memory_patterns=ignored_memory_patterns,
)
# resolve line ending (project -> global)
self.line_ending = project_config.line_ending or serena_config.line_ending
@@ -141,3 +141,11 @@ symbol_info_budget:
# list of regex patterns which, when matched, mark a memory entry as read‑only.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []
@@ -79,6 +79,15 @@ ignored_paths: []
# You can extend the list on a per-project basis in the project.yml configuration file.
read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# This is useful for projects with large numbers of archived memory files.
# You can extend the list on a per-project basis in the project.yml configuration file.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []
# timeout, in seconds, after which tool executions are terminated
tool_timeout: 240